单线程
PC、H5、混合开发(Hybrid)、Node
性价比超级高
javascript 编程语言
ECMAScript 标准(语法、规则)
DOM (Doument Object Model) 文档对象模型
BOM (Browser Object Model) 浏览器对象模型
Vue,React,jQuery,angular
推荐书籍
JavaScript权威指南
JavaScript高级程序设计
DOM编程艺术
JavaScript性能优化
JavaScript数据结构与算法
nodeJS深入浅出
了不起的nodejs
http://es6.ruanyifeng.com/ ES6
1.行内
<div onclick="alert(1)"></div>
2.内嵌
把script标签放在body结束标签的上方
3.外链
<script src="js的路径"></script>
注意:如果加了src,script中不能执行js了。
//输出一个js代码 //注释一行代码
/**/ 多行代码注释
document.getElementById(‘字符串’);
document.getElementsByTagName(‘标签名’) *获取一组元素
document.querySelector(”) 能够获取一个元素,就算是获取一组,也只能获取到一组中的第一个。
document.querySelectorAll() 能够获取一组元素
var oDiv1 = document.getElementById('oDiv');
var oDiv2 = document.getElementsByClassName('qqq')[0];//通过类名获取元素,我们得到的是一个集合,想要获取某个元素 需要用对应的索引来获取,索引都是从0开始的
var oDiv3 = document.getElementsByTagName('div');//通过标签名获取元素,得到的结果跟通过类名得到的结果一致
console.log(oDiv1, oDiv2, oDiv3[0]);
var str = oDiv2.innerHTML;
var str1 = oDiv2.innerText;
// innerHTML 可以识别html结构; innerText 不能识别html结构
// oDiv2.innerHTML = '<h1>珠峰</h1>';
oDiv2.innerText = '<h1>珠峰</h1>';
//str = '<h1>珠峰</h1>'; 不管用
console.log(str, str1);
oDiv2.className = 'www';
obj.属性名
读操作: console.log(obj.属性名)
写操作: obj.属性名 = 要设置的值
要操作元素的样式:
ele.style.某个样式 = ‘值’;
ele.className 操作类名
要操作元素内容:
input的内容: value
input.value = ‘哈哈’;
操作别的元素的内容:
innerText 操作文本 读操作,只能获得文本
innerHTML 操作结构,读操作,获取到某个元素下的所有内容(包括文本、标签)
src
href
<a href="javascript:;" id="a">1234</a>阻止默认跳转
scr,href不要直接用来进行判断
let 与 var 的区别:
1.let一个变量只能申明一次
2.声明之后才能使用(有暂存死区)
3.不做window映射
4.支持块级作用域
可变的量,把数据存储到自定义的名字中,为了复用,只要使用这个名字,就对应到等号后面的值
let :
声明变量的时候名字不能重复
不能以数字开头
名字不能用关键字
为了复用
**一般变量取名字用小驼峰命名法:
开头小写,字母与字母之间的开头大写**
例:getElementById
不可变的量,把数据存储到自定义的名字中,为了复用,只要使用这个名字,就对应到等号后面的值
const:
声明变量的时候名字不能重复
不能以数字开头
名字不能用关键字
一般在获取元素的时候会用
console.log 打印日志
console.dir 打印详细信息日志
alert 调用浏览器的默认弹框
点 从属关系 翻译成汉语 就是“的”
用 点 的地方我们可以用中括号[]
var oDiv = document['getElementById']('oDiv');
debugger;
oDiv.style.width = '200px';
oDiv['style']['height'] = '200px';
oDiv.style.color = '#fff';
oDiv.style.backgroundColor = 'black';
var tex = document.getElementById('tex');
tex.value = '222';
基本数据类型:操作值,就是单纯的赋值(简单类型、原始值)
引用数据类型:操作地址,就是赋址,改变A会影响B(复杂类型、引用值)
string 字符串
number 数字
boolean 布尔
null
undefined
’ ’ ” ” `` 成对显示的
有长度:length(只能读不能写)
获取字符串中的某个字符,使用下标
字符串的拼接:
小技巧:
先把设置一个默认值,然后咔咔加加 '+ +'
注意: 换行的时候使用字符串拼接
反引号(字符串模板)
语法: 反引号${放变量,放运算}反引号
任何数据 + 字符串 = 字符串
<input type="text" id="inp">
<button id="btn">按钮</button>
<div id="oDiv">
</div>
// 字符串 用单引或者双引号或者反引号包含的部分
// 双引号不能包含双引号,单引号不能包含单引号
// var str = "qq'q";
// var str2 = "w\"we";//把中间的双引号转译成了双引号本身
// var str3 = `珠\`峰`;
// var oDiv = document.getElementById('oDiv');
//oDiv.innerHTML = '<h1>好的</h1>';
//oDiv.innerHTML = 'str';//给的就是字符串'str'
//oDiv.innerHTML = str3;//给的是变量 str对应的值
//oDiv.innerHTML = str + str3 + str2;//字符串的拼接
// oDiv.innerHTML = '<h1>' +
// '<div>' +
// '珠峰' +
// '</div>' +
// '</h1>';
//模板字符串
// oDiv.innerHTML = `<h1>
// <div>
// str
// ${str}
// </div>
// </h1>
// `;
//反引号包含的字符串,我们想用变量时,需要使用${变量}的语法
//1、写法 引号(单双反)包含 有“\”代表转译
//2、字符串拼接 一种是直接+ 若是反引号 还可以通过${}
var btn = document.getElementById('btn'),
inp = document.getElementById('inp'),
oDiv = document.getElementById('oDiv');
btn.onclick = function () {
oDiv.innerHTML = `我是${inp.value};`;
inp.value = '';//清空inp
}
// btn.onclick = function () {
// // var val = inp.value;
// oDiv.innerHTML = '我是' + inp.value;
// }
把其他数据类型转化成数字类型
强制类型转换:
把数据转成数字 :Number()
能转长得像数字的字符串
空字符串为0
空白数组为0
布尔值:true:1,false:0
null:0
别的都是NaN
数字类型中 只有 0 和 NaN 转成布尔类型是false; 其他都是true
parseInt(字符串,进制数):把字符串转成整数10进制的数字
parseInt转数组时,数组中第一位长得像数字,能转第一位
parseFloat(字符串):把字符串转成数字,并且保留小数
NaN -> not a number 只要是非法的数学运算就会出现NaN
isNaN -> 是NaN返回true,否则false
isNaN(”) -> Number(”) -> 0 -> false
toFixed:返回结果是一个字符串
保留几位小数,会四舍五入保留位数+1的数字
比如:
10.322 -> 保留2位 -> 10.32
10.325 -> 保留2位 -> 10.33
隐式类型转换:
数字 + 字符串 = 字符串
数字 (-*除%) 字符串 = 数字
加+ _ / * %模
! 取反
== (相等)只比较值即可,不用比较数据类型,在比较的时候会进行数据转换
如果数字与字符串进行比较的时候,双方转成数字
如果字符串与字符串进行比较的时候,通过unicode编码进行比较
=== (全等)不但要比较值还要比较类型 性能比 == 要高
!= (不等)
&& 并且
A && B
1.在if中使用 && 的含义 A要成立,B也要成立
2.从顺序的角度上来说,A需要成立才会找B,如果A不成立就是A
|| 或者
A || B
1.在if中使用||如果A不成立那么会去找B,如果2个都不成立那么就为false
2.从顺序上来来说,如果A成立就不会去B了,如果A不成立那么会去找B。
: ? 三元(三目)
一元 -> typeof a;
二元 -> 1 + 1
a?a:b 判断a是否成立,如果成立就走a否则走b
%:
5/2余1
取余的技巧:
A > B 取余数
A < B 取A
// console.log(3621%3);
// console.log('20' < 30);
// console.log('3' > '2000');
// console.log('3' !== 3);
//number 整数 小数 NaN
var n = 1;
var n2 = '1' + 2;//字符串12
var n3 = 1 + 2;//数字的3
console.log(n2, n3);
var n4 = '1' + 2 + 3;//'12'+3 '123'
var n5 = 1 + 2 + 3;//3+3 6
console.log(n4, n5);
//对于+号:两边只要有一个是字符串 那么就是字符串拼接;
var n6 = 1 + 2 + 3 + '4' + 5;//3+3+'4'+5//6+'4'+5//'645'
console.log(n6);
var n7 = '12' - 2;//10 先把两边的内容转成(Number)数字,再进行运算
var n8 = 12 - 2;
var m1 = Number('');//0
var m2 = Number('12q');//NaN
//Number 转化的时候,字符串中有非数字(不包含小数点),结果就是NaN
//四则运算 + - * / % 除了+ 其他的都会把符号两边转化成数字再去运算
var m3 = '12q' + 3;
var m4 = '12q' - 3;//NaN-3 -->NaN
一个 = 是赋值,
两个 == 是相对比较,三个 === 是绝对比较
// '1' == 1;//true
// '1' === 1;//false
// NaN == NaN//false
// 'NaN' == NaN//false
// //NaN 和谁都不相等
// 'NaN'=='NaN'//true
isNaN(Number('m2f4'));//isNaN(NaN) //true
NaN+1+'2'//NaN+'2' //NaN2
//NaN和任何数字做运算 结果都是NaN
parseInt parseFloat
parseInt('12.3')//12
parseInt('23.3q')//12
//从左向右查看,遇到非数字(包含小数点)即停,获取到的是前边的数字部分
//若第一位是非数字,则结果就是NaN
Number('')//0
parseInt('')//NaN
parseFloat('12.3')//12.3
parseFloat('1q2.3')//1
//从左向右查看,遇到非数字(不包含小数点)即停,获取到的是前边的数字部分
//若第一位是非数字,则结果就是NaN
0.1+0.2==0.3//false
Number('12.3q')//NaN
Number() Number('') Number([]) Number(null) //0
Number({}) Number(undefined) //NaN
isNaN('12.3q')//先用Number处理 括号内的参数
parseInt()
parseFloat()
把其他数据类型转化成布尔类型
数字类型中 只有0 和NaN转成布尔类型是false 其他都是true
字符串中 只有空字符串转成布尔是false,其他都是true
两个特殊的null和undefined是false
值类型 基本数据类型
除了 0 NaN '' null undefined 这五个值是false,其他都是true
! 取非、取反 先把感叹号后边的内容转化成布尔类型,然后再去取反
!! 取反再取反,结果相当于用了一个Boolean,等于没取反
var bol = !1; //false
var bol2 = !''; //true
var bol3 = ![]; //false
null 空指针对象
一般在学习、开发中遇到null的情况基本上是元素没有获取到
undefined 未定义,有五中种情况会出现undefined
1.变量定义未赋值
2.对象没有某个属性的时候(对象没有属性是不会报错的)
3.函数没有return,默认为undefined
4.函数的形参数没有传实参
基本数据类型的自定义属性
当页面加载完成执行函数内的代码。
window.onload = function(){
const box = document.getElementById('box');
console.log(box.style);
}
// console.log(Object.prototype.__proto__);
引用数据类型:数组 对象 函数 正则 日期Date Math…
纯对象:只能是new Object出来的
new Object
{ }
实例对象的原型链 === 构造函数的原型为Object.prototype
obj._proto_ === Object.prototype
Object.getPrototypeOf(obj) 找到指定对象的构造函数的原型
键值对 属性名:属性值
属性值 可以是 任意数据类型
属性名 都是字符串类型
定义对象:
标准写法:
let obj = {
"name":"小红"
}
不过属性名可以不写引号
var obj = {
name:"小红",
age:9,
1:2,
_:2,
$:3,
};
// var 1 = 2;
var _ = 1234;
var $ = 123;
console.log(obj[1])
获取属性值 有两种方式
一种是打点的方式,另一种是 [] 的方式
若属性名是 数字; 那么我们只能使用[]的方式
obj.1 错误
打点的方式 完全等价于 [字符串]
console.log(obj.age)
console.log(obj['age']);
var age = 'name';
console.log(obj[age]);// obj['name'] '小红'
console.log(obj['age']) // obj.age 9
console.log(obj[obj[age]])//obj['小红']
// 对象中, 若没有对应的属性时,获取到的就是一个 undefined
var qqq = obj.qqq;
qqq === undefined
console.log(qqq+'');
var a = 12;
var b = a;
b = 13;
console.log(a);// 12
string、number、undefined、boolean、object、function
相比ECMAScript的数据类型,是多了一个function,少了null(为object)
console.log(typeof 1) // ‘number’
console.log(typeof ‘1’) // ‘string’
console.log(typeof true) // ‘boolean’
console.log(typeof undefined) // ‘undefined’
console.log(typeof null) // ‘object’
console.log(typeof typeof 1) // ‘string’
undefined === undefined //true
console.log(b === undefined);
instanceof:左值是不是右值构造出来的,是就为true,否则false;右值是不是左值的老爹
let arr = [1,2,3];
1.console.log(arr instanceof Object);
2.console.log( Object.prototype.toString.call(arr) === '[object Array]' )
3.console.log(Array.isArray(arr));
delete obj.age; //删除某个属性就用delete
1.let i=0 只走一次
2.判断条件
3.进循环体
4.设置下次循环的条件
2->3->4 2->3->4
for(let i=0;i<6;i++){
}
注意:
遇到for嵌套for的时候,里面的变量名不要和外面的变量名一样
或者使用let
大循环进一次,执行完小循环。
循环中的跳过:continue
循环中的跳出:break
for(let i=0;i<6;i++){
if(i===3){
// continue
// break;
};
alert(i);
}
1.判断条件
2.条件成立进循环体
3.设置下次循环的条件
while(条件){
}
1.通过枚举对象身上的属性名,来做到循环的目的
2.循环的次数跟对象的属性的个数有关系
3.只要是遍历对象的情况下,使用for in循环
let obj = {
width:'100px',
height:'200px',
background:'red',
border:'1px solid #000'
}
for(let attr in obj){
box.style[attr] = obj[attr];
}
代码块,为了复用。
定义:
1.函数声明
如果函数不带名字,那么属于匿名函数, 直接写匿名函数是会报错的。
如何避免报错呢?
1.声明函数时要有名字
2.匿名函数要为一个表达式
2.函数表达式
3.类声明
new Function
调用:
函数名 + 括号 fn()
事件调用 onclick = function(){}
一般是赋值一个匿名函数或者是赋值一个函数(名)地址
定时器调用
setTimeout(function(){},1000);
setInterval(function(){},1000);
定义函数的时候开辟了一个新的堆内存空间,把函数内的代码转成字符串,存到堆内存中,再把空间地址赋值给变量或者函数名
调用开辟一个执行栈,把代码块中的代码复制到执行栈中执行,参数赋值,执行上下文直到销毁变量
函数参数:
在函数声明的时候放在括号内的自定义名字(形式上的参数)
function fn(a){} a就为形参
实参:在函数调用的时候放在括号内的随意数据说(实实在在的参数)
fn(1) 1就是实参
实参能传所有类型的数据
实参内能传若干个数据,一个与一个之间用逗号分隔,且可以为所有的数据类型
return
函数执行有两件大事莫忘:
1.执行函数
2.函数返回值
函数内有return返回值就为return后面的值,如果没有return就是undefined
不管是函数还是方法,都有返回值。
function fn(){
let a = 10;
return a;
}
console.log(fn());
return下面的代码会被中断
return也可以在函数中的循环下,终止循环
让函数外使用函数内的运算结果时,使用return。
alert(alert(5)); //=>5 undefined
arguments是一个在函数内的类数组(长得像数组,但是不是真数组)
一般是实参有很多个,能用到arguments
arguments:只要是一个函数都有arguments,是一个实参的类数组集合,有length,能使用下标操作,但是不能使用数组的方法。
arguments的某个是跟形参一一对应的。
函数:
function fn(){}
方法:
let obj = {
fn:function(){
}
}
fn(); 函数前面没有主,没有点
obj.fn(); 方法前面有主,有.
1.元素没有获取到
2.在正则中搜索不到字符就会为null
3.原型链的末端
function fn(){
console.log(this);
}
let obj = {
fn:function(){
console.log(this);
},
fn2:function(){
}
}
// fn(); //window
// obj.fn(); //obj,.前面的
// let box = document.getElementById('box');
// console.log( box );
let str = '2345ndsja';
// console.log(str.match(/a/));
// console.log(Object.prototype.__proto__);
凡是能用点的地方都能用[]
不过[]内要么是变量,要么是属性名(字符串)
点 -> 只有属性名才用.
( )提权:
(1+1)*2
括号中的就变成了表达式
匿名函数自执行
注意: 在匿名函数的前面加上分号,不然容易报错。
前自增++num;
后自增num++;
当输出或者比较某个变量的时候,是变量与变量进行比较
前自增,在找到变量之前就被处理过了,找到变量是被处理过后的变量
后自增,在找到变量之前什么事儿都没发生,找到的变量就为非处理过的值
// alert( num ++); //1
// alert(++ num); //2
// console.log(num++ == ++num ); //1 3 false
// console.log(++ num == num++);//2 2 true
// console.log(num);
btn.onclick = function(){
ul.innerHTML += '<li>'+ (++num) +'</li>';
}
if判断
if(条件成立){
执行这里代码
}else{ 否则执行这里
}
switch
switch (变量) {
case 2: (条件)
console.log('语句一')
break; (中断判断)
default:(默认)
break;
}
注意:
break必须写,不然会穿透(当一个条件成立之后,还会执行下面的代码)
能用switch一定能用if判断,能用if判断的地方不一定能用switch
定义:就是给某个对象(元素)加上一个自定义的“变量”
目的:为了让这个“变量”跟某个对象(元素)进行对应。
当需要操作一个元素的时候去对应另一个元素(数据), 那么就要想到索引(自定义属性)。
obj.属性名 || obj[‘属性名’]
let a = 10; 变量,前面没有.
obj = {
a:10
}
obj.a 属性,前面有.
function fn(){} 函数
fn()
obj = {
fn:function(){} 方法
}
obj.fn()
this 在事件中,一般事件触发谁,this就是谁
事件触发的时候,this就是触发的元素
onclick 点击
onmouseover 移入
onmouseout 移出
btn.num = 0;
btn2.num = 0;
btn.onclick = fn;
btn2.onclick = fn;
// function(){
// btn2.innerText = '按钮('+ (++num2) +')';
// }
function fn(){
this.innerText = '按钮('+ (++this.num) +')';
// console.dir(this.num); //这个
}
box.属性有2种方式是可以直接获得到的
1.设置了一个 box.zdy = 0;
console.log(box.zdy)
2.元素对象默认的属性
box.id
在行间设置自定义属性
ele.setAttribute(‘属性名’,’属性值’)
ele.dataset.属性名=属性值 写操作
在行间获取自定义属性
ele.getAttribute(‘属性名’)
ele.dataset.属性名读操作
数组写法:
let arr = []; 性能要高
let arr = new Array();
有length,既能读也能写
数组中获取每个值通过下标去操作它 [1,2,3][number]
数组的最后一个一定是数组.length-1
把数组清空:arr.length = 0;
要知道数组中要什么方法,直接console.dir数组
去看 __proto__下的方法即可。
给数组的最后一位添加,一个或者多个数据,一个与一个之间用逗号分隔
返回值为新数组的长度,改变原数组。
// let arr = new Array();
let arr = [1,'好的',true,function fn(){}];
// arr.length = 5;
// arr[4] = '100';
或者arr[arr.length]=12 基于原生键值对的方法,也可以向末尾添加一项新的内容
let a = arr.push(5,2);
console.dir(a);
往数组的最后一位删除一个数据
返回值为删除的那个,改变原数组
ary.length–; //=>ary.length=ary.length-1;
往数组的首位添加一个或者多个数据
返回值是新数组的长度,改变原数组
基于原生ES6展开运算符,把原有的ary克隆一份,在新的数组中创建第一项,其余的内容使用原始ary中的信息即可,也算实现了向开始追加的效果
let ary=[2,30];
ary=[100,...ary];
console.log(ary) //=>ary=[100,2,30]
往数组的首位删除一个数据
返回值是删除的那个,空即为undefined,改变原数组
delete ary[0]
能够增删改查数组,根据参数的不同,结果就不同
返回值为被删除数组,无删除则不返回,改变原数组
删除:(2个参数)
第一个参数就是从数组的第几位起(选择数组的起始位置)
从0开始计数
第二个参数就是操作几个数据(删除几个)
返回值就是删除的那几个,把删除的部分用新数组存储起来返回
let arr = [1,2,3,4];
console.log(arr.splice(0,2));//从0开始,删除2个
清空数组:
let res=ary.splice(0);只写1个参数会删除到末尾,基于这种方法可以清空一个数组,把原始数组中的内容以新数组存储起来(类似数组的克隆)删除第一项
ary.splice(0,1)删除最后一项
ary.splice(ary.length-1,1)
添加:(从哪开始添加,0,添加的数据)
第一个参数就是从数组的第几位起(选择数组的起始位置)
从0开始计数
第二个参数
就是操作几个数据(是否替换)
如果是替换,写替换几个数字
第三个参数:(或者以上)
添加多个
let arr = [1,2,3,6,7,5,6,7,8];
从5开始,一个都不删除,添加6和7
最后的结果是:
[1,2,3,6,7,5,6,7,8]
arr.splice(3,0,6,7);
//向数组末尾追加
ary.splice(ary.length,0,'qwe')
//向数组首位添加
ary.splice(0,0,'ww')
替换:(n,m,替换的数据)
第二个参数:替换几个
let arr = [23,213,12];
arr.splice(0,2,212);
console.log(arr);
从数组的第0位开始,删除23和213,替换成212
结果:
[212,12]
注意:
如果第2位为0,那么返回值为空数组,
也就是说,第二位不为0,返回值就是你删除的那(几)个数据
返回的是数组。
forEach:遍历数组中的每一项内容,专门用来循环数组的。
两个参数:
第一个参数:
函数-> function(){}
function(数组中的每个值,索引值,整个数组){
}
第二个参数:
改变this指向,写啥是啥(如果写个null,undefined还是为window)
返回值是undefined,不会改变原数组。
let arr = [true,'haha',10,{},[1,2,3]];
arr.forEach(function(item,i,all){
// console.log(item);//数组中的每项
// console.log(i); //索引
// console.log(this);
},arr);
循环数组
返回值为新的数组,原数组不会改变。
function(item,i,all){
return 新数组的每项
}
let arr = [1,'你好','哈哈','呵呵'];
let newArr = arr.map(function(item,i,all){
// console.log(item,i,all)
return '<li>'+ item +'</li>'
});
console.log(newArr);
// console.log();
ul.innerHTML = newArr.join('');
参数:
function(item,i,all){
return 条件成立的某项
}
在函数中,只有条件成立的结果才能返回到数组中
return必须是true
返回值不会改变原数组
//过滤大于28的数字
let arr = [18,28,38,48,26];
let arr2 = arr.filter(function(item,i){
if(item > 20 && item <= 28){
return item;
}
// return item > 20 && item <= 28;
});
// let arr3 = arr.filter(e=>e > 20 && e <= 28);
console.log(arr3);
toString()
每一项用逗号分隔
返回转换后的字符串,原来数组不变
let ary=[1,2,3];
let arr=ary.toString();
console.log(arr); //=>'1','2','3'
join()
以某个字符串为分隔符,指定分隔符
返回值一个字符串,不改变原数组
注意:
如果不需要分隔符,必须使用空字符串表示”
let arr = ['你','好','吗']; //'你|好'
console.log(arr.join(''));
// console.log(arr.join().length);
// console.log(arr[0]+arr[1]);
eval() 把字符串变成js表达式
let ary=[1,2,3];
let res=ary.join('+');//=>'1+2+3'
eval(res); //=>1+2+3=6
颠倒数组顺序
排列后的新数组,改变原数组
[1,2,3] -> [3,2,1]
// let arr = [1,2,3];
// arr.reverse();
// console.dir(arr);
let str = '您迎欢峰珠'; //转成'珠峰欢迎您' console.log(str.split('').reverse().join(''));
indexOf / lastIndexOf(检测当前项在数组中第一次或者最后一次出现位置的索引值)在IE6-8不兼容
参数:要检索的这一项内容
返回值:这一项出现的位置索引值(数字),如果数组中没有这一项,返回的结果是-1
原来数组不变
let ary=[10,20,30,20,40,20];
console.log(ary.indexOf(20)); //=>1
console.log(ary.lastindexOf(20)); //=>3
inCludes() 判断是否包含
返回结果:包含就为true,不包含为false
查看数组中某项数据是否满足某个条件,
只要有一个符合条件就返回true,
如果所有项条件都不成立,返回false
返回一个布尔值
let arr = [1,2,3,4,5];
//查看数组中是否有6,明显没有,就返回false
console.log(arr.some(function(item){return item===6}))
判断数组中是不是每一项都符合某个条件
全部都符合返回true,只要有一项不符合就返回false
参数:
function (item,i,all){
}
//就想知道,这个数组中是否所有项都为true
let arr = [true,true,true,true,false];
let a = arr.every(function(item){
return item;
})
console.log(a);
sort默认排序是按照unicode编码来排序的
也可以使用自定义排序
sort中需要传入一个函数,让a,b,是正数就交换位置,是负数就不交换位置
a-b就是从小到大排序
b-a就是从大到小排序
返回新数组,改变原数组
let arr = ['2px',3,4,7,1,6,12];
arr.sort(function(a,b){
return parseInt(a)-parseInt(b);
});
console.log(arr);
连接一个或者多个数组
返回值为新的数组,不改变原数组
就算是没有数组连接
比如:
arr.concat() -> 克隆一份数组
let arr2 = [1,2,3];
let arr3 = [4,5,6];
console.log(arr2.concat(arr3,[7,8,9]));
slice(n,m);都是数字,从索引n开始,找到索引为m的地方(不包含m这一项)
//m不写找到末尾
ary.slice(1);//数组的克隆,参数0不写也可以
ary.slice(0);
slice(包含起始位置,结束位置但不包含结束位置)
返回值为新数组
不会改变原数组。
let arr = [1,2,3,6,5];
console.log(arr.slice(2,4));//->[3,6]
console.log(arr);//->[1, 2, 3, 6, 5]
字符串: ” “” ``
页面中获取出来的内容都是字符串
(value、innerHTML、inerText、href、src、className、id、width…)
new String(‘123’)
字符串的length只可读不可写
字符串的方法基本上是不改变原字符串的。
以字符为分隔符,把字符串分割为数组。
返回值是数组。
如果没有分割符,使用空字符串去切,
如果什么都不传,把整个字符串放到数组中
// let str = '珠-峰'; //->
// console.log(str.split('-')); //-> ['珠','峰']
let str = '珠峰'; //-> ['珠','峰']
console.log(str.split(''));
substring(0,0)
从哪里开始截取,到哪里结束,但不包含结束位置
如果你只传一个参数,那么就是从哪开始,截取到字符串的最后
substr(0,0) 从哪开始截取几个
// let str = new String('123');
// console.log(str);
// let str = 'dsjdsadsandkjwyque01k';
// str.length = 1;
// console.log(str.length);
// console.dir(str);
// console.log(str.substring(3,9));//dsadsa
找到指定字符首次出现的位置,返回出来索引,找不到-1
let str = 'xsdmufGxw';
// console.log(str.indexOf('x'))
// console.log(str.indexOf('x',2))
// console.log(str.indexOf('z'))
console.log(str.includes('z'));
console.log(str.toUpperCase())
console.log(str.toLowerCase());
console.log(' dsadjsadsa '.trim())
stringObject.replace(regexp/substr,replacement)
日历、倒计时、时钟
时间对象:用户本地的时间(所以说不安全)
new Date()
年:date.getFullYear()
月:date.getMonth()+1
获取出来的时间比当前月份少1,所以要加1
日:date.getDate()
周几:date.getDay() 周日为0
时:date.getHours()
分:date.getMinutes()
秒:date.getSeconds()
let date = new Date();
// console.log(date.getFullYear());
// console.log(date.getMonth()+1);
// console.log(date.getDate());
// console.log(date.getDay());
// console.log(date.getHours())
// console.log(date.getMinutes());
// console.log(date.getSeconds());
// console.log( new Date() )
setInterval(函数,指定时间(毫秒),函数的实参) (连续炸)
每间隔一段指定时间,就执行一次函数
返回值为数字(即为的编号)。
clearInterval(编号) :清除
setTimeout(函数,指定时间(毫秒),第一个函数的实参)
当到指定的时间时,只执行一次函数
返回值为数字(即为的编号)。
clearTimeout(编号); :清除
let timer=null,timer2 = null;
btn.onclick = function(){
// timer = setTimeout(function(){
// console.log('bong,bong');
// },2000);
timer2 = setInterval(function(){
console.log('bong,bong,就不信炸不死!');
},2000);
}
btn2.onclick = function(){
// console.log(timer,timer2);
// clearTimeout(timer);
clearInterval(timer2);
}
未来的时间 - 现在的时间 = 剩下的时间
12:30 - 12:10 = 20
Math.floor(11.638683333) -> 11 向下取整
Math.ceil(11.638683333) -> 12 向上取整
//时间对象中可以放数字,月份以当前月份 - 1
let d = new Date(2019,3,10,12,30); //未来的时间
function fn(){
let d2 = new Date(); //当前的时间
// console.log(d - d2); //毫秒来计算的
// 848206
let s = (d - d2)/1000;
let m = Math.floor(s/60)
box.innerHTML = m+'分'+Math.floor(s%60) +'秒';
// console.log();
}
fn();
setInterval(fn,1000);
第一种公式:
天:86400 = 60*60*24 : Math.floor(t/86400)
小时:Math.floor(t%86400/3600)
分钟:Math.floor(t%86400%3600/60)
秒:Math.floor(t%60)
第二种公式:
天 Math.floor(t/86400);
t%=86400;
小时 var h=Math.floor(t/3600);
t%=3600;
分钟 var m=Math.floor(t/60);
秒 t%=60;
常用的取整公式
x ~ y : Math.round(Math.random()*(y-x) + x)
0 ~ x : Math.round(Math.random()*x)
1 ~ x : Math.ceil(Math.random()*x)||1
0 ~ x-1 : Math.floor(Math.random()*x)
时间对象中可以放数字,月份以当前月份 - 1
let d = new Date(2019,3,10,12,30); //未来的时间
为什么要自己调用自己?
一个函数里面有相应的运算或者是逻辑处理
需要这个处理重复执行,所以才需要自己调用自己。
注意:递归容易死循环,所以需要写上递归中终止条件
遇到Maximum call stack size exceeded报错,就是递归死循环
let n = 10;
function fn(n) {
if (n == 1) return 1;
return fn(n - 1) + n;
}
console.log(fn(n));
通过document提供了一些api,能够赋予开发者操作页面的能力
当通过document提供的api获取到元素的时候,获取到的元素是个对象,它跟页面的标签是相映射的关系,也就是说,通过操作对象的属性能够操作标签。
DOM树 -> 由节点组成。
难点关系:
父子关系
parentNode 父节点
childNodes 所有子节点(包括元素节点、文本节点、注释节点)
children 获取某个元素下的元素子节点。
祖孙关系
兄弟关系:
previousElementSibling上一个兄弟节点
nextElementSibling 下一个兄弟节点
没有就是null
第一个娃 firstElementChild
最后一个娃 lastElementChild
去查看节点类型的
标签->元素节点:数字1
属性节点:数字2
文本节点:数字3 文字、换行
注释节点:数字 8
document:数字9
提示:您可以使用 length 属性来确定属性的数量,然后您就能够遍历所有的属性节点并提取您需要的信息。
document.createElement(‘li’)
往元素的末尾添加元素
parent.appendChild(child);
往元素的首位添加元素
parent.insertBefore(插入的元素,参照元素);
如果添加了true,不但能克隆元素本身,还克隆子级
注意:在chrome的73.0.3683.103中多次克隆报错
百度搜MDN查看math属性的使用方法
Math 是一个内置对象,处理数学问题,比如:四舍五入 取整 随机数等等
Math.round() 四舍五入
正数中 .5是进
负数中 .5是舍
console.log(typeof Math.round) // "function"
Math.round(1.5) // 2
Math.round(-1.5) //1
Math.ceil() 向上取整,有小数,整数部分就加1
一定比之前大
Math.ceil(2.1) // 3
Math.ceil(12.1) // 13
Math,ceil(-12.8) //-12
Math.floor 向下取整
Math.floor(2.1) // 2
Math.floor(1.9) // 1
Marh.floor(-12.8) //-13
Math.abs([number value]) 一般放数字,获取绝对值
绝对值永远是正数或者0
传递的不是数字类型值,先基于Number()默认转成数字
Math.abs(-1) //1
Math.abs(true); //1
Math.max() 获取一堆数中最大值
Math.max(3,4,7,16) //16
Math.max([2,5,1,6]) //NaN 此处只传一个值,是个数组,和内置的语法要求不符
Math.max不可以直接对数组进行求值
var arr = [4, 1, 3, 12, 11]
console.log(Math.max(arr))
Math.min() 获取一堆数中最小值
Math.min(3,4,7,16)//3
Math.random() 获取0-1之间的随机小数
方法返回介于0到1之间一个随机小数,包含0,不包括1(包前不包后)
Math.random() //0.8458846320507001
获取n-m之间的随机小数
Math.random()*(m-n)+n
1到10之间 随机小数
Math.random() * (10 - 1) + 1
获取n到m之间的随机整数(包含n也包含m n<m)
Math.round(Math.random() * (m - n) + n)
1到10之间 随机整数
Math.round(Math.random() * (10 - 1) + 1)
0到m之间的随机整数
Math.round(Math.random() * m)
获取0到10之间的随机整数
Math.round(Math.random() * (10 - 0) + 0)
Math.round(Math.random() * 10)
Math.round(Math.random() * 100)
Math.PI 圆周率 3.1415926
Math.sqrt/pow()
sqrt:给一个数开平方
Math.sqrt(16) //=>4 (符合N*N=M 这样的M才能整开平方)
Math.sqrt(-16) //=>NaN (负数开不了平方)
pow:计算一个数的多少次幂
Math.pow(2,10); //=>1024
ES6 扩展运算符 …对象 将后面的对象展开,新开一个地址
[4, 1, 3, 12, 11] => 4 1 3 12 11
console.log(...arr) // 4 1 3 12 11
console.log(Math.max(4, 1, 3, 12))
console.log(Math.max(...arr)) // 12
执行栈,在script标签内第一层js代码
(1)如果当前script中的全局没有某个变量,
这个时候还会向上面的script中去查找(只会向上找,默认不会向下找)有就输出,否则报错。
(2)如果有多个script标签,上面的script中的代码报错,是不会影响,下面script标签内的代码执行的。
(3)全局的this为window
(4)从作用域链的角度来说,最终会找到widnow下有没有某个属性(var的情况)
(5)多个script标签如果都用了let,那么同样走let特性(不能有重名变量)
(6)使用var的时候等同于在widnow下注册了一个属性,并且在没赋值之前为undefined
不过在chrome|FF下 在变量的上方打印window的时候会有属性值结果,要注意的是,显示出来的是骗人的(跟undefined走)
(7)函数默认也是挂在window身上的
(8)变量必须加var或者let来声明,不然在变量没赋值之前访问这个变量就报错
在函数执行栈中运行代码,函数中的变量和参数,会默认处理在函数内部,不会被外界所干扰。
如果函数内的计算或者逻辑处理需要被外界所接受, 一般使用return。
防止全局污染(封闭空间,教科书上说它就是闭包,但是,这样不一定是我们眼中的闭包)
{}
let、const 识别块级作用域
var不识别
要小心function(){}
1.当前域没有会去它的上级域查找,
直到window结束,window都没有就报错.
2.变量提升
当代码在执行前会把var和function提前进行解析
并且变量赋值为undefined,函数赋值代码块.
3.执行上下文
我只看等号赋值
4.如果在函数中,参数赋值比执行上下文要提前(也可以理解为,函数比正常执行多了一个参数赋值)
5.赋值参数问题
如果实参是简单类型,函数内再怎么搞都不会影响原值
如果实参是个引用类型,函数内直接改形参的&&值&&那么会影响原值;
但是如果在函数内赋值了一个新的对象,那么此时赋值之后再怎么改参数,都不会影响原值
一种函数的简写方式
1.let fn = () => {} 没有参数
2.let fn = (a,b,c) => {} 多个参数
3.let fn = a => {} 一个参数
4.let fn = a => a; return a
箭头函数的this,跟它的执行上下文有直接的关系。
箭头函数的this找老爹。
function fn(){
console.log(arguments);//[1,2,3,4,5]
}
fn(1,2,3,4,5)
箭头函数中没有arguments
函数形成一个不销毁的作用域,这个作用域就叫闭包
函数套函数,子函数使用父函数的参数或者变量
并且子函数被外界所引用,此时父级形成闭包环境
父级的参数或者变量不被浏览器垃圾回收机制回收.
此时,打印父函数的返回值,有个属性为Scopes
Scopes下有个closure的属性,closure 就是闭包。
作用
使用闭包可以一直存储父级的参数或者变量不被外界的函数或者变量所干扰(污染)
可以减少全局污染和代码冗余,使代码能够更加精简,达到高内聚低耦合的效果
github
https://github.com/
程序员交友网站、进行代码托管、去加入一些知名项目的开发、自己静态的页面…
设置秘钥:
1.进入github
2.找到你的头像,点击选择settings
3.在最左边有SSH and GPG keys
生成秘钥: ssh-keygen -t rsa -C “你的邮箱名” 去生成秘钥(一堆方块)
点击鼠标右键,选择git Bash here
输入: ssh-keygen -t rsa -C “这里换上你的邮箱”
此处无脑回车,会出现方框
git config - -global user.name “这里换上你的用户名”
git config - -global user.email “这里换上你的邮箱”
输入: ssh -T git@github.com
只要出现Hi ….就说明绑定成功
如果有兴趣,可以自己研究一下hexo搭建自己的博客
1.git init(第一种)
2.到github网站中新建项目 (推荐使用)
步骤:
1.点击头像旁边的 +号 选择 New repository
2.填写项目名称、描述、勾选readme、点击创建按钮
3.找到clone按钮,点击复制
4.在你想管理的文件夹下打开git工具,输入git clone 刚才复制的地址
可以使用ctrl+v, shift+insert,点击鼠标右键,选择粘贴
git status 查看当前你的版本的状态
按方向键上 能够出来刚才输入的命令
按tab键可以自动补全文件
git add 文件名
如果已经输入过一次add,那么可以使用git add .的方式快速
把多个文件放到暂存区。
git commit -m “注释(这个注释是为了方便用户查找)”
git commit -a -m “注释”
git diff
git diff –cached
git diff master
git log
git reflog
git reset - -hard 版本号
版本号通过查看版本去找
当代码已经形成版本之后,想把代码提交到远程仓库(github、gitlab、码云…)
可以使用
git push origin master
git config - -global credential.helper store
因为要从别人的代码保管库中去拿代码,所以要知道保管库的地址(比如github),还要使用git(要通过git去获取保管库中的资源)
设置贡献者
配置本地提交的用户名和密码
git config –global user.email “你提交分支的时候提交记录里显示的用户邮箱”
git config –global user.name “提交分支的时候提交记录里显示的用户名”
git config –global user.name 查看用户邮箱
git config –list 查看所有配置项
检测是否关联:
ssh git@github.com
通过git clone 去拿到项目
1.先fork项目(去项目上点击fork按钮)
2.克隆项目到本地
3.修改代码并提交代码
4.到你fork的项目上点击new pull request
5.create pull request
1.先fork项目(去项目上点击fork按钮)
2.克隆项目到本地
3.开分支
创建分支:
git branch 名字
查看分支:
git branch,带*号说明当前分支位置
切换分支:
git checkout 分支名
快速创建分支并且切换:
git checkout -b 分支名
合并分支:
git merge 分支名
在合并代码的时候有可能会出现冲突,会进入MERGING状态
需要手动的去删除或者添加代码,然后再进行一次版本的控制(使MERGING状态变成分支状态,这个时候就合并成功)
4.修改代码并提交代码
第一次使用create pull request
提交过一次之后,作者会保留合并信息(修改者多次提交,作者都能看到)
查看远程仓库地址:
git remote -v
撤销:
git checkout – 文件名
git reset HEAD 文件名
git commit –amend -m “注释”
git reset –hard HEAD|版本号(回滚)
如果说有白名单,在提交的时候代码冲突
那么git pull
直接把远程仓库的代码和本地就进行了合并(但是不一定有效,有时候会直接覆盖)
用git fetch 把远程的代码下载下来
可以通过 git diff origin/master 去查看差异
用git merge master origin/master 合并
手动解决冲突,然提交在版本区,在push
单例 — 单独的实例
实例:把相同的事务总结(归纳、抽象)出来,形成一类事务,把描述事务的属性和方法具体化,这个具体的描述的对象就是实例
命名冲突:
1.封闭空间
把一段代码放到一个函数内,当执行函数的时候
函数内的域和外界是互不干扰的
(function(){})()
2.命名空间
把一些变量或者函数变成某个对象下的属性和方法,对象与对象之间空间地址是不一样的,所以可以解决命名冲突的问题
单例模式的优势:
1.解决命名冲突
2.把相同事务归为了一类,并且把这些属性或者方法,放到了一个堆内存空间中存储。
3.模块化的开发
让其复杂,那就需要高级单例模式
使用一个函数自执行函数,这个函数返回一个对象
高级单例模式,可以实现高内聚、低耦合
目的是批量生成多个实例,
通过传参去描述具体的实例,
把生产后的对象返回到外界使用。
把属性或者方法挂在this上,然后去new这个函数
浅规则是构造函数首字母大写。
new 是一元运算符 -> 专门运算函数的
new之后发生了什么?
1.执行函数,不使用()调用也是可以执行函数的,此时的()只是为了传参
2.构造函数(fn)中的this指向了当前实例。
跟普通函数比较把默认的window转成当前实例
function Fn(){} 构造函数
fn{} 实例化对象
3.return 的结果默认指向当前实例this,
有return 如果后面跟着的是一个基本类型
结果依然是实例,如果后面跟着的是一个引用
类型,那么结果就是这个return后的引用类型
实例下的constructor == 实例的构造函数
但是这个constructor是随时随地随便可以修改的,constructor只能当作实例中指向构造函数的一种参考物,并不能左右实例的构造函数真相。
constructor什么时候会被修改?
给构造函数的原型赋址对象的时候会变
解决:
手动修正constructor指向
{
constructor:构造函数
}
把描述相同的事务抽象出来,归为一类,把描述这个类的属性和方法挂在这个类的原型(prototype)上的一种编程方式就叫面向对象
抽象:抽离出长的相像的部分。
js的面向对象有特征:
抽象
封装
继承
多态类 -> 构造函数 -> 把相同的代码抽离出来归纳在一个函数中
定义:原型是函数的一个属性prototype(当声明一个函数的时候自身带有的一个属性,这个属性一般只给它的实例化对象使用)
原型:prototype为 一个函数天生自带的属性,它的值是一个对象
为了优化性能的,prototype只给它的实例化对象使用
如果在相同的类中去new多个实例,它们的方法是相等的
它的用处是如果实例化对象上没有某个属性或者方法,还会去这个实例化对象的构造函数中的原型下去查找,该属性或者方法.
如果构造函数的原型上没有这个方法,那么还会去原型下的原型链(proto)中查找,找到Object.prototype
构造函数的原型下的方法只给它的实例化对象使用
以构造函数模式 + 原型模式 = js面向对象模式
调用原型上的方法
Fn.prototype.say
new Fn.say()
定义:实例身上都有的一个属性_proto_,这个属性指向构造函数的原型;
实例化对象上的原型链 === 构造函数的原型;
实例对象上没有,就会通过原型链找到构造函数的原型;
构造函数的原型又是一个对象,如果在这个对象下还没有;还会通过构造函数的原型的原型链进行查找,最后直到找到Oject.prototype为止。
实例上一定有原型链
实例化对象上的原型链 === 构造函数的原型
实例化对象._proto_ === 构造函数.prototype
对象上有原型链 -> 指向构造函数的原型
1.先看对象自身有没有这个属性或者方法
有就不找了
没有的话就接着找
2.通过对象的原型链找构造函数的原型
有就不找了
没有的话就接着找
3.因为函数的原型是个对象,对象身上有原型链,通过原型链又去找构造函数原型
有就不找了
没有的话就接着找
所以说,函数即是函数,又是对象(它是Function的实例化对象)
函数即有原型,也有原型链,函数的原型上的属性或者方法只给它的
实例化对象使用。
普通函数
function fn(){}
构造函数(类)构造对象的函数 得使用new
function Fn(name){
this.name = name;
}
实例化对象 new Function的实例化对象
let fn = new Function();
只要遇到函数,this就有可能变
window:
1.直接在全局输出this
2.函数打印this,并且直接调用
3.定时器中普通函数this为window
4.匿名函数自执行
5.某些回调
事件中的this:
哪个对象触发,this就是那个对象
事件中尽量不用箭头函数,按钮会找不到,体验不好
实例:
new 构造函数 -> this就是实例
箭头函数:
this就走定义箭头函数的域
箭头函数不能new,一new就报错
箭头函数也没有arguments
方法的this就是“.”前面的对象
arr.fn = function(){
console.log(this);
}
arr.fn(); //[1,2,3,4]
在严格模式下为undefined
function fn(){
"use strict"
console.log(this);
}
fn();
一个函数,天生就自带一些属性和方法
其中有:
1、call():
有无数的参数
第一个参数:改变this指向(写啥是啥)
null undefined为window
第二个参数之后:就是实参
2、apply():
有2个参数
第一个参数:改变this指向(写啥是啥)
null和undefined为window
第二个参数:数组[1,2,3],数组中放参数
3、bind():
有无数个参数
第一个参数:改变this指向(写啥是啥)
null和undefined为window
第二个参数之后:就是实参
使用bind不能立马执行函数,会返回一个新函数,这个函数 的this是改变了的,得执行这个新函数才能输出代码。
/*
bind(this,无限个参数)
改变this指向,返回一个函数,函数才会执行
1.改变this
2.传参
3.如果是构造函数的话,this不需要被改变
*/
function fn(a,b,c){
this.a = a;
console.log(this);
}
Function.prototype.bind = function(context,...arg){
//arg为剩余的参数集合为数组
let that = this;//函数实例
//bind返回的函数
function bound (...arg2){//arg2为bound的所有参数
//bound是不是this的构造函数,如果是,说明new了
//是构造函数this就不能变
if(this instanceof bound){
that.apply(this,[...arg,...arg2]);
}else{
//让arg和arg2合并
that.apply(context,[...arg,...arg2]);//把数组放到apply的第二个参数上
}
}
//在new bound的时候,让bound的实例拥有fn原型上的方法
if(this.prototype){
bound.prototype = this.prototype;
}
return bound;
}
fn.prototype.aaa = 555;
// fn.bind = function(){}
let f = fn.bind(document,1,2);
console.log(new f().aaa);
// console.log(new f(3).aaa);
// console.log(f);
//第一个需求
// Function.prototype.bind = function(context){
// let that = this;//函数实例
// //bind返回的函数
// function bound (){
// that.apply(context);
// }
// return bound;
// }
//第二个需求
// Function.prototype.bind = function(context,...arg){
// //arg为剩余的参数集合为数组
// let that = this;//函数实例
// //bind返回的函数
// function bound (...arg2){//arg2为bound的所有参数
// //让arg和arg2合并
// that.apply(context,[...arg,...arg2]);//把数组放到apply的第二个参数上
// }
// return bound;
// }
Object.prototype.toString.call(arr)
每个数据类型都有自己的toString方法
每个toString方法都不一样
正好Object的toString方法能够显示当前的数据类型
类数组转数组的方法:
Array.prototype.slice.call(arr);
Array.form(arr);
取数组大小值:
console.log(Math.max.apply(null,ary));
console.log(Math.max(…ary))
function fn(){
console.log(this);
}
fn.call('你的');
定义:当简单类型去使用某个属性或者方法的时候,内部会偷偷地转成对象(new 内置类)把属性或者方法提供使用者,然后再悄悄的销毁,这个过程就叫包装对象。
只有在引用类型下才能添加属性和方法
obj.hasOwnProperty(‘属性名’)
for in不但会枚举本对象,还会枚举原型,此时就好多出来一些莫名其妙的东西,但是我们不想要不是对象身上的东西
obj.hasOwnProperty(‘属性名’)
查看某个属性是不是对象自身的
是就返回true,不是就返回false
JSON -> [] | {}
JSON 只是一种数据格式 ‘{}’ | ‘[]’
JSON.parse()
能够把json转成对象或者数组
json必须是一个标准格式的json,不然转不出来
json中不能放函数、不能为undefined
'{"name":12,"nn":"ds"}'
JSON.stringify()
把对象或者数组转成json
对象中不能放函数、不能为undefined
JSON.parse(JSON.stringify(arr)); 深拷贝:先把数组或者对象转成字符串,再转成数组或对象,这样就不是同一个地址
低版本可以使用json2.js
eval 能够把字符串尽量转成js能执行的代码。
new Function('','console.log()')
请求JSON
fetch(url) url-> 文件路径
.then(d=>d.text())
.then(arr=>{
//arr = eval('('+arr+')');
console.log(arr);
})
子类继承了父类的一些特征,然后自己还有一套自己的特征
为什么要继承:
就是为了代码能够更好复用,组合起来生成一个新的类别
属性继承:类式继承(构造函数继承)
把父类看做一个函数,调用这个父类并且通过call去改变this指向,把指向改为子类。
function Child(){
Parent.call(this)
}
class式
class Child extends Parent {}
方法继承:
扩展式继承
{…父类原型}
child.prototype={...Parent.prototype}
拷贝继承
一、浅拷贝定义:一个引用类型赋值的时候,只赋值第一层的简单类型,这样两个空间地址不一样,改变一个空间的属性是不会影响另一个空间地址的属性的;
把一个对象中的第一层的简单类型赋值给另一个对象
child.prototype = Object.assgin(parent.prototype)
let obj2 = Object.assgin(obj)
let obj2 = {...obj}
for(let attr in obj){
obj2[attr] = obj[attr];
}
对象的属性只能有一个,如果写多个,下面的会把上面的覆盖
Object.assign(对象1,对象2,对象3….)
从后往前合并,改变第一个对象,第一个对象可以为{}
Child.prototype = Object.assign({},parent.prototype)
console.log(Object.assign({},obj,obj2,obj3));
二、深拷贝定义:把一个对象中的第一层有引用类型,那么赋值的时候改变一个会影响另一个(因为赋值的过程还是赋值地址),需要只要发现引用类型就深入到内部去找简单类型,直到对象中的所有属性都是简单类型为止。
child.prototype=deepclone (Parent.prototype)深度克隆(深拷贝)
function deepClone(obj) {
//先声明一个数组,去存克隆出来的内容
//判断obj是否为数组,是数组就o就为[],否则为{}
let o = obj.push ? [] : {};
//循环传进来的对象
for (let attr in obj) {
// for(let i=0;i<arr.length;i++){
//判断对象中的某个值是否为引用类型
//如果是,就继续调用deepClone把引用值传到函数中
if (obj.hasOwnProperty(attr)) {
if (typeof obj[attr] === 'object') {
o[attr] = deepClone(obj[attr])
} else {
//如果是简单类型就直接赋值
o[attr] = obj[attr];
}
}
}
return o;
}
let arr2 = deepClone(arr);
console.log(arr2);
原型继承
例:
function Ph(){
Ph.prototype=Fruit.prototype;
Coconut.prototype=new Ph;
}
寄生式继承
child.prototype = Object.create(parent.prototype)
必须传入一个对象
返回值为一个新的对象,这个对象的原型链指向传入的参数
就是把对象中的方法挂在新对象的原型链上,括号中的对象变成了新的对象,而这个对象的原型链指向构造函数的原型;
这个构造函数是传递进来的那个。
Coder.prototype=Object.create(Person.prototype);
Coder子类 Person父类
class (类) 保留字
let 关键字
class是ES6才出来的新语法,优势就是写起来方便;它其实还是之前构造函数的语法糖
https://segmentfault.com/a/1190000010159725
class 类名 { }
使用constructor去接收参数
constructor(a,b){ super下面才能写this,不然就报错,super括号中传入父类使用的参数}
class Cat extends Animal 继承用extends
写方法: 直接在类中
方法名 () { }
设置静态方法
static shui(){ }
…
1.扩展运算符
let arr = [1,2,3,4];
console.log(...arr);
function fn(){
console.log(arguments); //...[]
function f(a,b){
console.log(a,b);
}
f(...arguments);
}
fn(1,2);
2.剩余运算符
...c 是个数组
function fn(a,...c){
console.log(a,c);
}
fn(1,2,3,4,5);
扩展运算符和剩余运算符的应用
class Person {
constructor(name,age) {
this.name = name;
this.age = age;
}
}
class Women extends Person {
constructor(fs,...arg) { //剩余运算符
super(...arg); //扩展运算符
console.log(arg)
this.sex = '女';
this.fushi = fs;
}
}
let w = new Women('爱穿裙子','马骅骏',18);
console.log(w);
正则(对象):
概念:专门用来检索(检查、搜索)模糊范围字符串的一种规则。
特征:
懒惰性:\d 让它找一个一定不会找第二个
贪婪性:\d+ 有多少就尽量匹配多少
字面量–//:/ 不带引号的字符串 / -> /a/ 找字符串’a’
实例化:new RegExp(‘字符串’,’修饰符’),里面可以放变量,返回的是正则
例:new RegExp('\\b'+ sCLASS +'\\b'); // \b是边界符,前面再加\是转义
正则的方法:
exec[Ig’zek]:/规则/.exec(字符串),找到正则中第一个匹配的字符,并且放到数组中。
捕获一项
[字符、index、input、groups]
test[test]:/规则/.test(字符串)
检测正则中的规则是否匹配字符串,成立就返回true,否则false
字符串用正则的方法:
string.match(/正则/) 捕获一项或多项
找到正则中所有匹配的字符且放到数组中。
没匹配到就是null
let str = 'x1j89321Xeu218nX2d32';
//全局查找整个字符串,把为数字的存在数组中
console.log(str.match(/\d/g));
// 全局查找整个字符串,把一个数字或者连续多个数字的存在数组中
console.log(str.match(/\d+/g));
string.replace(要替换的字符串 || /正则/,替换成什么 || 函数);
当第二个参数为函数的时候
默认情况:
函数的第一个参数就是每次匹配到的结果;
函数的第二个参数是index,也就是索引
函数的第三个参数input,也就是所有项
函数的第四个(及以后)参数都是undefined
此函数必须要有return,不然就是undefined
返回为新的字符串
修饰符:
g -> global 全局
i -> ignore case 忽略大小写
m -> 多行匹配
量词 : + 最少1个最多无限
“.” -> 任意一个字符,除回车(\r)
“ \” 转义符:需要注意的是在写 “ \” 记得转义,在字符串中需要转义
元字符:
当 “ \ + 字母 ” 的时候会有特殊含义,这种带有特殊含义的,我们叫元字符。
\n 换行
\r 回车“\r\n 回车并换行”
\d 一个数字
\D 一个非数字(不是数字)
\s 一个空格
\S 一个非空格
\w 一个数字、字母、下划线
\W 一个非数字、字母、下划线
\b 一个边界符
\B 一个非边界符
“ | ”或者
( )小括号
1.( ) 提权 (1+1)*1
2.( ) 子项: 能够在一个规则中提取某些(指定)字符
子项如果包了一个规则,在规则之后有量词,那么结果为最后一个
比如:
let str='2019'
(\d)+ -> 找到的数字不是2是9
子项的顺序是从左往右数的,每有一个()就是一个子项
子项的个数代表着形参的个数(从第二个开始计算)
从第二个参数起子项与形参一一对应
let str = '2019----4/-/-/-//26'; //2019年4月26日
let s = str.replace(/(\d+)\D+(\d+)\D+(\d+)/,function($0,$1,$2,$3){
// console.log($3);
return $1 + '年' + $2 + '月' + $3 + '日';
});
console.log(s);
子项重复项:
比如:
/(d1)c1(b1)\1\2/
当前有2个子项,一个是d1另一个是b1
\1就代表第一个子项,\2就代表第二个子项
let str = '437826357384623537463363726573384633278467382';
let min = -Infinity;
let i;
//把字符串变成有序的
str = str.split('').sort().join('');
// console.log(str);
//比较大小 $0就是当前匹配的 , $1 子项
str.replace(/(\d)\1+/g,($0,$1)=>{
if(min < $0.length){
min = $0.length;//求出多少次
i = $1;// 它是谁
}
});
console.log(min,i);
解题思路:
比如:[].push()
这个方法是什么 (Array的方法)
有什么用,(用来向数组的最后一为添加一个或者多个数据)
参数传的是什么(至少是一个数据,也可以是多个数据)
返回值是什么(新数组的长度)
[ ] 中括号 :例:[ab]要么是a,要么是b
在中括号中任意选择一个字符
比如:[12345] 可以为1也可以为2也可以为3…
也可以简写为[1-5],因为 - 是1到5的意思
为什么能那么写呢?
因为顺序是按照unicode编码来的
数字是:[0-9]
小写英文:[a-z]
大写英文:[A-Z]
从大A到小z: [A-Za-z]
中文区间范围 :[\u4e00-\u9fa5]
let str = 'hello!~张欢,您老贵庚?'; //21
let num = 0;
for(let i=0;i<str.length;i++){
if(/[\u4e00-\u9fa5]/.test(str[i])){
num += 2;
}else{
num ++
}
}
console.log(num);
^ 开头、非
开头:/^\d/
排除:[^3465] 排除3465
$ 结尾
let str = '180';
str.match(/^1[89]$/)
意义:整段字符串是否都满足正则的规则,而不是字符串中有一段匹配正则的规则就返回
{ } 范围
{n,m}:最小n次,最大m次
{n}:最大是n,最小也是n
+ -> {1,}:最小一次,最多无限
? -> {0,1}:最小0次,最多1次
“ * ” -> {0, }:最小0次,最多无限次
1、qq:首字母不能是0,全部都要是数字,最小5位,最多11位 -> /^[1-9]\d{4,10}$/
2、手机号:1[3456789]\d{9}
getComputedStyle(element).属性
获取到的结果为带单位的字符串,比如:100px
ele.clientWidth/ele.clientHeight
支持padding,不包含边框
元素可视区宽度 ,不带单位的数字
如果设置一个固定值,就以固定值为依据显示,不会以被内容撑开显示
ele.offsetWidth/ele.offsetHeight
支持padding,也包含边框,不带单位的数字
如果设置一个固定值,就以固定值为依据显示,不会以被内容撑开显示
ele.scrollHeight/ele.scrollWidth
被内容撑开的高度(不包含边框)
不管设不设置固定样式,都以被内容撑开为显示结果。
document.documentElement.clientWidth/document.documentElement.clientHeight
不包含滚动条
window.innerWidth/window.innerHeight (BOM)
包含滚动条
注意:
window.innerWidth||window.innerHeight如果有滚动条,是忽略了滚动条的尺寸的 1280
document.body.clientWidth(浏览器的尺寸,排除滚动条的) ,使用的时候把默认样式清除 1263
offsetParent 定位父级,没有就按照body走
offsetLeft/offsetTop 当前元素的左|上外边框到定位父级的左|上的内边框的距离
如果要使用offsetLeft/offsetTop的属性,一定要做到以下几点:
1.子级有绝对定位
2.定位父级也一定要有定位
3.子级和父级都要有宽高(触发haslayout,zoom:1)
getBoundingClientRect( )top/left( ) 当前元素到页面可视区的尺寸、距离
width/height/left/right/top/bottom/x/y
注意:是跟滚动条走的。
clientLeft/clientTop 边框尺寸
获得绝对位置:
let obj = div1;
let ot = obj.clientTop
let t = 0;
while(obj){
t += obj.offsetTop + obj.clientTop;
obj = obj.offsetParent;
}
t - ot
window.pageYOffset/window.pageXOffset
滚动条的距离,只能读不能写
window.scrollTo(x,y)
专门用来写滚动条距离的,只写
document.documentElement.scrollTop/document.documentElement.scrollLeft
滚动条的距离,既能读也能写(document.documentElement=HTML)
打开新的窗口
window.open(url,用什么方式打开_blank,_self)
注意:需要用户主动触发才不会被拦截
比如: window.open('http://www.baidu.com')
关闭浏览器窗口,最好也是用户主动触发体验才好
window.close( );
window.onresize 缩放浏览器的时候触发的事件
window.onscroll 有滚动条的时候滚轮时触发
window.navigator.userAgent 查看用户的浏览器内核信息
注意:使用的时候判断的字符串有可能会被模拟。
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36
操作系统的版本,位数
window.location.hash 即能读也能写 –重点
hash: “” 浏览器hash信息 (#及之后的信息),更换这个信息是不会刷新页面的
host: “” ip地址 + 端口号
hostname: “” ip地址
href: “” url地址 –重点
window.location.href 在当前页面中跳转页面
origin: “file://”
port: “” 端口 –重点
protocol: “file:” 协议 –重点
reload: ƒ reload() 刷新页面 –重点
replace: ƒ () 替换页面
search: “” 查询信息 –重点
window.location.search –重点
?到#号之间的信息
即可读也可写,只不过写的时候会刷新页面
http://www.zhufengpeixun.cn?num=1#page=0
window.onhashchange 当hash值发生变化的时候就触发
window.onhashchange = function(){
alert('触发');
}
window.location.reload( ) 刷新页面 –重点
延迟加载的原理:
可视区的高度 + 滚动条的高度如果大于等于图片的绝对位置,就加载把图片的src赋值。
防抖:高频率不触发,停下来才触发
节流:每隔一段时间触发
//工具中的绝对位置
class Tools {
po(ele){
let obj = ele,
top = 0,left = 0;
while(obj){
top += obj.offsetTop;
left += obj.offsetLeft;
obj = obj.offsetParent;
}
top -= ele.clientTop;
left -= ele.clientLeft;
//new Tools().po(ele).l
return {
top,
left
}
}
}
//实例化Tools
let t = new Tools();
//可视区的高度
let winH = window.innerHeight;
//获取页面中所有li和img
let lis = document.querySelectorAll('li');
let img = document.querySelectorAll('img');
scrollPic();
//当滚轮的时候触发
window.onscroll = scrollPic;
function scrollPic(){
//滚轮事件
//拿到滚动条的距离
let scrollT = window.pageYOffset;
//循环每个li。
//看一看滚动条的距离 + 可视区的高度
//是否大于等于每个li的绝对Top位置
for(let i=0;i<lis.length;i++){
if(scrollT + winH >= t.po(lis[i]).top){
//如果是那么就把图片的pic中的路径取出来
//赋值给img的src
if(!img[i].src){
// console.log(i);
//oimg就是背锅侠,就为了试试图片是否正常
let oimg = new Image;
oimg.src = img[i].getAttribute('pic');
oimg.onload = function(){
img[i].src = img[i].getAttribute('pic');
//删除pic
img[i].removeAttribute('pic');
//异步队列
setTimeout(()=>{
img[i].style.opacity = 1;
});
}
oimg.onerror = function(){
img[i].src = './img/timg.jpg';
//删除pic
img[i].removeAttribute('pic');
//异步队列
setTimeout(()=>{
img[i].style.opacity = 1;
});
}
}
}
}
// console.log(scrollT + winH,t.po(lis[6]).top);
}
onload 图片加载成功的时候触发
onerror 图片加载失败的时候触发
如果发现某张图是坏图,那么在onerror中重新赋值一个好的(预备)图片即可
let timg = new Image;
timg.src = './img/1.jpg';
timg.onload = function(){
// alert('好图');
img.src = './img/1.jpg';
}
timg.onerror = function(){
// alert('坏');
img.src = './img/timg.jpg';
}
元素身上有个classList对象
add(类名,…) 添加类名
在元素中添加一个或多个类名。
如果指定的类名已存在,则不会添加
remove(类名) 移除类名。
移除元素中一个或多个类名。
注意: 移除不存在的类名,不会报错。
replace(替换谁,替换成什么) 替换
contains(类名) 判断指定的类名是否存在
返回布尔值
true - 元素已经包含了该类名
false - 元素中不存在该类名
toggle(类名, true|false) 在元素中切换类名。
返回值是布尔值
第一个参数为要在元素中移除的类名,并返回 false。
如果该类名不存在则会在元素中添加类名,并返回 true。
第二个是可选参数,是个布尔值用于设置元素是否强制添加或移除类,不管该类名是否存在。
移除一个 class: element.classList.toggle(“classToRemove”, false);
添加一个 class: element.classList.toggle(“classToAdd”, true);
注意: Internet Explorer 或 Opera 12 及其更早版本不支持第二个参数。
document.onclick = function(){
// addClass(box,'red');
// box.classList.add('red');
// box.classList.replace('bg','red');
// console.log(box.classList.contains('bg'))
console.log(box.classList.toggle('bg'));
console.log(box.classList);
}
function addClass(obj,cname){
let cn = obj.className;
if(!cn.split(' ').includes(cname)){
cn += ' red';
obj.className = cn;
};
}
ActionScript -> AS
t : time 已过时间
b : begin 起始值
c : count 总的运动值
d : duration 持续时间
requestAnimationFrame(函数) 动画帧
返回值为number(编号)
cancelAnimationFrame(编号) 关闭动画帧
匀速运动:
b + c * (t/d)
回调( 钩子函数 ) -> 在做某件事情(某个条件成立)的时候调用函数
点击 onclick
在按下的时候 + 抬起的时候 = 点击
onmousedown -> 鼠标按下
onmouseup -> 鼠标抬起
1、每个元素天生都有很多属性、方法及事件,如果没有事件函数,赋值此时事件的默认值为null
2、如果有系统内置的事件默认值为null,自定义的事件为undefined
3、当用户操作浏览器的时候,用户被当做被监听者,浏览器一直监听者用户的操作,会触发若干个事件,如果某个事件上有事件函数,那么就被执行
DOM0级事件
on事件 + 事件函数为原始的事件绑定方式
解除事件将某个元素的事件赋值为null
ele.事件 = null
1级DOM–(为什么没有1级DOM)
DOM级别1于1998年10月1日成为W3C推荐标准。1级DOM标准中并没有定义事件相关的内容,所以没有所谓的1级DOM事件模型。在2级DOM中除了定义了一些DOM相关的操作之外还定义了一个事件模型 ,这个标准下的事件模型就是我们所说的2级DOM事件模型
DOM2级事件
事件绑定:
ele.addEventListener(‘不带on的事件名’,函数,是否捕获)
事件解除:(重要的是解除绑定)
ele.removeEventListener(‘不带on的事件名’,有名函数(和绑定函数一个地址),是否捕获)
btn.addEventListener('click',c.bind(this));
function c(){
alert(1);
//绑定和解除绑定不是同一地址,所以解除不了
btn.removeEventListener('click',c);
}
onclick 点击
ondblclick 鼠标双击事件
onmouseover 移入
onmouseout 移出
onmousedown 鼠标按下
onmouseup 鼠标抬起
onscroll 有滚动条的时候滚轮时触发
onload 加载完后
onerror 异常加载 - > img.onerror img.onload
onhashchange 当hash值发生变化的时候就触发
onresize 缩放浏览器的时候触发的事件
onload 和ready的区别:
(DOMContentLoaded,onreadystatechange)
从捕获阶段 到 目标阶段 再到 冒泡阶段 的过程称为事件流。
先执行捕获再执行冒泡
如果事件绑定的是目标元素,那么是按照绑定事件函数的先后顺序来依次执行(跟捕获冒泡没关系)
从window 到 目标点的阶段叫捕获阶段。
在捕获的过程中,如果上级和下级(祖先级关系)绑定同一事件函数;那么先触发上级的在触发下级的
DOM0捕获不到捕获阶段,只有冒泡阶段。
box1.addEventListener(不带on的事件名,function(){
alert('red');
},是否捕获);
捕获就为: true
冒泡就为:false (默认)
从目标点由下而上 直到window 叫做冒泡阶段 。
在这个过程中,如果上级和下级同理有祖先关系,绑定同一事件函数 先触发目标 再触发目标的上级,这整个过程叫做事件流 事件模型
祖先级关系 是套个多层
事件函数中的第一个参数,默认为事件对象
事件对象:
当用户触发某个事件的时候,记录用户操作页面的一些细节信息
阻止冒泡:(目的是为了不让自己冒泡触发祖先级的同一事件)
ev.cancelBubble = true; 它不是一个标准,但是所有浏览器都支持
ev.stopPropagation(); 它是标准,但是在低版本IE下是不兼容的
chrome/IE
window中都有一个event
FF中是没有event对象的
let onoff = true;
btn.onclick = function(ev){
if(onoff){
box.style.display = 'block';
btn.innerHTML = '隐藏'
}else{
box.style.display = 'none';
btn.innerHTML = '打开'
}
onoff = !onoff;
// alert(1);
// ev.cancelBubble = true;
ev.stopPropagation();
// console.log(ev)
}
document.onclick = function(){
box.style.display = 'none';
onoff = true;
btn.innerHTML = '打开'
}
ev.target 目标点(事件源)
在嵌套关系中,给上层元素绑定事件,可以通过事件源查到事件触发的对象(元素)
tagName 查看标签名 注意:大写
jq中有个delegate原理就是事件委托。
ul.onclick = function(ev){
// console.log(ev.target)
if(ev.target.tagName === 'LI'){
console.log(ev.target.parentNode.children)
let lis = ev.target.parentNode.children;
for(let i=0;i<lis.length;i++){
console.log(lis[i]===ev.target,lis[i],i);
if(lis[i] !== ev.target){
lis[i].style.background = '';
}else{
ev.target.style.background = 'green';
}
}
}
// console.log(ev.target.tagName)
}
onmouseenter 移入
onmouseleave 移出
box.onmouseenter = function(){
alert('box移入')
}
box.onmouseleave = function(){
alert('bo移出')
}
box1.onmouseenter = function(){
alert('box2移入')
}
基于浏览器可视区的顶端
ev.clientX 横轴 数字类型
ev.clientY 纵轴 数字类型
基于body的
ev.pageX 横轴
ev.pageY 纵轴
onkeydown 键盘按下 获取不到最新的value值
onkeyup 键盘抬起 可以获取到最新的value值
ev.keyCode 键码 获取的值为数字类型
37-40 键盘的左上右下
空格 32
特殊按键:
ev.ctrlKey
ev.shiftKey
ev.altKey
只要按住就为true,否则false
//第一种2个键值写法
let code = false;
txt.onkeydown = function (ev) {
if (ev.keyCode === 17) {
code = true;
console.log(code);
}
}
txt.onkeyup = function (ev) {
if (ev.keyCode === 13 && code) {
console.log('bong');
}
if (ev.keyCode === 17) {
code = false;
}
}
//第二种2个键值写法。
txt.onkeyup = function (ev) {
if (ev.keyCode === 13 && ev.shiftKey) {
let li = document.createElement('li');
li.innerHTML = this.value;
//下面这种写法和上面这种写法结果一样
ul.insertBefore(li, ul.children[0]);
txt.value = '';
}
}
当按键的时候会停顿一下,这个停顿有助于提高用户体验,避免重复输入
let numL = 0;
let numT = 0;
let timer = null;
document.onkeydown = function (ev) {
clearInterval(timer);
timer = setInterval(() => {
switch (ev.keyCode) {
case 39: //右
numL += 10;
box.style.left = numL + 'px';
break;
case 40: //下
numT += 10;
box.style.top = numT + 'px';
break;
case 37: //左
numL -= 10;
box.style.left = numL + 'px';
break;
case 38: //上
numT -= 10;
box.style.top = numT + 'px';
break;
}
}, 22);
}
document.onkeyup = function () {
clearInterval(timer);
}
默认行为:
没有人为主动设置,浏览器默认的行为。
行为使用:
可以设置布尔值来控制是否阻止默认行为
ev.returnValue = false; 都支持
return false; 只支持DOM0
ev.preventDefault(); DOM2支持
注意:
如果在document身上阻止默认行为,会把整个页面的默认行为都阻止掉。
如果想阻止某部分元素的默认行为,那么在指定的元素下阻止即可。
// document.onkeydown = function(ev){
// // console.log(ev);
// // ev.returnValue = false;
// return false;
// }
document.addEventListener('keydown',function(ev){
// ev.returnValue = false;
// return false;
ev.preventDefault();
});
img.onmousedown = function(ev){
ev.returnValue = false;
}
oncontextmenu 事件可以阻止右键菜单
document.onmousedown = function(ev){
// console.log(1);
ev.returnValue = false;
}
oninput: 改变输入框的内容就触发
txt.oninput = function(){
size.innerHTML = this.value.length + '字';
}
聚焦事件: onfocus()
失去焦点事件: onblur()
自动聚焦: ele.focus()
select() 选中文本框的内容 (它也有聚焦的功能)
onchange 内容改变的时候触发(内容一定要不一致才触发)
let arr = ['0.jpg','1.jpg','2.jpg']
sele.onchange = function(){
// console.log(this.value);
div.innerHTML = arr[this.value];
}
// txt.onfocus = function(){
// console.log('聚焦');
// }
// // txt.focus();
// txt.select();
// btn.onclick = function(){
// txt.select();
// }
// txt.onblur = function(){
// console.log('失焦');
// }
onscroll 滚轮事件 (页面必须要有滚动条)
onmousewheel (Chrome)没有滚动条的滚轮事件(只要鼠标滚轮就触发)
ev.wheelDelta 可以检测滚动的方向
大于0, 为上滚轮
小于0, 为下滚轮
DOMMouseScroll (FF的滚轮)(只能使用DOM2来进行绑定)
ev.detail 可以检测滚动的方向
小于0, 为上滚轮
大于0, 为下滚轮
document.onmousewheel = function(ev){
if(ev.wheelDelta > 0){
alert('向上');
}else{
alert('向下');
}
console.log(ev.wheelDelta);
}
document.addEventListener('DOMMouseScroll',function(ev){
// alert('Gunlun');
alert(ev.detail);
})
把页面中的所有静态资源加载完成之后触发window.onload
// window.onload = function(){
// alert(1);
// }
DOM加载完成才触发的事件
jQ: ready() $(function(){ })
DOMContentLoaded 高版本
onreadystatechange 低版本IE都支持这个事件
在低版本IE下,document有个doScroll的方法
document.documentElement.doScroll(‘left’)
这个方法在DOM没有加载出来之前是没有的,也就是说调用该方法会报错,换句话来说,只要DOM记载成功就有doScroll方法
在try中包裹可能会报错的代码,包裹之后不会影响下面代码执行,只要捕获到错误,那么会进catch
try{
document.documentElement.doScroll('left')
console.log('DOM加载完成');
}catch(e){
再次调用try、catch直到进try
}
例如:
try{
console.log(a);
}catch(error){
a = 10;
console.log(error);//字符串
}
alert(a);
实现的原理:改变元素top值和left值
拖拽三大事件:这3个事件写拖拽在PC上是需要嵌套的。
onmousedown
onmousemove
onmouseup
//存按下的坐标的
let l = 0;
let t = 0;
let ol = 0;
let ot = 0;
box.onmousedown = function(ev){
l = ev.pageX; //按下的坐标
t = ev.pageY;
//每次按下的时候去记录元素的初始距离
ol = box.offsetLeft;
ot = box.offsetTop;
box.onmousemove = function(ev){
//移动的坐标(ev.pageX) - 按下的坐标(l)
// console.log()
box.style.left = ol + ev.pageX - l + 'px';
box.style.top = ot + ev.pageY - t + 'px';
}
box.onmouseup = function(){
box.onmousemove = null;
}
}
let num = 0;
//按下的时候
box.onmousedown = function(ev){
//拿到那一小段距离(按下的位置到边界位置)
/*
按下的位置:
ev.pageX
ev.pageY
元素的初始位置
box.offsetLeft
box.offsetTop
按下的位置 - 元素的初始位置 = 拿到那一小段距离
问题:
1.当mousemove在元素身上的时候,鼠标移动过快会导致
鼠标脱离元素,脱离元素就不能让元素移动
解决:
把元素身上的mousemove换到document身上
2.当mouseup的时候如果鼠标不在元素身上,那么
解除move行为也就失效了
解决:
把元素身上的mouseup换到document身上
3.如果在mouseup的时候只清除move函数的行为
那么在document进行up的行为还会执行
解决:
在清除mousemove的时候也把mouseup清除
*/
let disX = ev.pageX - box.offsetLeft;
let disY = ev.pageY - box.offsetTop;
//为了保证移动过元素的
let onoff = false;
document.onmousemove = function(ev){
//只要移动过元素就设置为true
onoff = true;
//在move的过程当值就可以求出元素的位置
/*
移动时的坐标 - 那一小段距离 = 盒子当前的位置
*/
box.style.left = ev.pageX - disX + 'px';
box.style.top = ev.pageY - disY + 'px';
}
document.onmouseup = function(){
//只要移动过就进行累计
if(onoff){
num++;
console.log(num);
}
//在鼠标抬起的时候清除move
document.onmousemove = null;
document.onmouseup = null;
}
return false;
}
box.addEventListener('mousedown',function(ev){
let disX = ev.pageX - this.offsetLeft;
let disY = ev.pageY - this.offsetTop;
let move = function(ev){
box.style.left = ev.pageX - disX + 'px';
box.style.top = ev.pageY - disY + 'px';
}
function up(){
document.removeEventListener('mousemove',move);
document.removeEventListener('mouseup',up);
}
document.addEventListener('mousemove',move);
document.addEventListener('mouseup',up);
// ev.returnValue = false;
ev.preventDefault();
});
/*
在拖拽元素的时候,要注意该元素的一个定位问题
父级要有相对定位,自己有绝对定位
如果没有相对定位,在限制范围的时候还要减去box的offsetLeft和border的尺寸
*/
box2.onmousedown = function(ev){
let disX = ev.pageX - box2.offsetLeft;
let disY = ev.pageY - box2.offsetTop;
console.log(ev.pageX,box2.offsetLeft)
box.onmousemove = function(ev){
let l = ev.pageX - disX;
let t = ev.pageY - disY;
if(l < 0){
l = 0;
}else if(l > 2 + box.offsetLeft + box.clientWidth - box2.clientWidth){
l = 2 + box.offsetLeft + box.clientWidth - box2.clientWidth;
}
if(t < 0){
t = 0;
}else if(t > box.clientHeight - box2.clientHeight){
t = box.clientHeight - box2.clientHeight;
}
box2.style.left = l + 'px';
box2.style.top = t + 'px';
}
document.onmouseup = function(){
box.onmousemove = document.onmouseup = null;
}
}
/*
1.在按下box的时候,创建一个跟它一样一样的元素
2.拖动一样一样的那个元素
3.抬起的时候把一样一样元素的位置给按下的那个box
4.再删除一样一样的那个元素
*/
box.onmousedown = function(ev){
let disX = ev.pageX - box.offsetLeft;
let disY = ev.pageY - box.offsetTop;
//创建一个元素
let createBox = document.createElement('div');
createBox.className = 'active';
//为了第二次点击的时候一样一样这个元素的位置不为0
//所以把box的初始位置给一样一样这个元素
createBox.style.left = box.offsetLeft + 'px';
createBox.style.top = box.offsetTop + 'px';
// for(let attr in getComputedStyle(box)){
// createBox.style[attr] = getComputedStyle(box)[attr];
// }
//插入到页面
body.appendChild(createBox);
// console.log(createBox)
document.onmousemove = function(ev){
let l = ev.pageX - disX;
let t = ev.pageY - disY;
//移动一样一样那个元素
createBox.style.left = l + 'px';
createBox.style.top = t + 'px';
}
document.onmouseup = function(){
//把一样一样那个元素的位置给box
box.style.left = createBox.style.left;
box.style.top = createBox.style.top;
//删除一样一样那个元素
createBox.remove();
document.onmousemove = document.onmouseup = null;
}
//阻止默认行为
return false;
}
/*
比例:
0 - 1
1/1
box2的top / (黑色的高度 - 红色的高度) = 0到1之间的比例
*/
box2.onmousedown = function(ev){
let disY = ev.pageY - box2.offsetTop;
document.onmousemove = function(ev){
let t = ev.pageY - disY;
if(t < 0){
t = 0;
}else if(t > box.clientHeight - box2.clientHeight){
t = box.clientHeight - box2.clientHeight;
}
let scale = t / (box.clientHeight - box2.clientHeight);
console.log(box4.scrollHeight)
/*
box4的top值 = 比例 * (被内容撑开的高度 - 内容可视区的高度)
*/
box4.style.top = - scale * (box4.scrollHeight - box3.clientHeight) + 'px';
box2.style.top = t + 'px';
}
document.onmouseup = function(){
document.onmousemove = document.onmouseup = null;
}
}
/*
1.加滚轮
2.页面的内容是否小于内容可视区的高度,就隐藏滚动条
3.内容越多,滚动条越短,内容越少滚动条就越长
*/
let b4h = box4.scrollHeight; //box4的被内容撑开的高度
let b3h = box3.clientHeight;//内容可视区的高度
//如果box4的高度小于等于可视区的高度,那么隐藏滚动条
if(b4h <= b3h){
box.style.display = 'none';
}
/*
内容越多,滚动条就越短,否则就越长
最小30,最长150
30 + box3高度/box4高度 * 120
*/
let h = 30 + b3h/b4h * 120;
// if(h < 30){
// h = 30;
// }else if(h > 150){
// h = 150;
// }
box2.style.height = h + 'px';
box2.onmousedown = function(ev){
let disY = ev.pageY - box2.offsetTop;
document.onmousemove = function(ev){
let t = ev.pageY - disY;
if(t < 0){
t = 0;
}else if(t > box.clientHeight - box2.clientHeight){
t = box.clientHeight - box2.clientHeight;
}
let scale = t / (box.clientHeight - box2.clientHeight);
console.log(box4.scrollHeight)
/*
box4的top值 = 比例 * (被内容撑开的高度 - 内容可视区的高度)
*/
box4.style.top = - scale * (box4.scrollHeight - box3.clientHeight) + 'px';
box2.style.top = t + 'px';
}
document.onmouseup = function(){
document.onmousemove = document.onmouseup = null;
}
}
addWheel(box,function(o){
let t = box2.offsetTop;
if(o){
t -= 5;
}else{
t += 5;
}
if(t < 0){
t = 0;
}else if(t > box.clientHeight - box2.clientHeight){
t = box.clientHeight - box2.clientHeight;
}
let scale = t / (box.clientHeight - box2.clientHeight);
box4.style.top = - scale * (box4.scrollHeight - box3.clientHeight) + 'px';
box2.style.top = t + 'px';
});
function addWheel(obj,fn){
//let w = window.navigator.userAgent.toLowerCase();
// if(w.includes('chrome')){
if(obj.onmousewheel === null){
// if('onmousewheel' in window){
obj.onmousewheel = whell;
}else{
obj.addEventListener('DOMMouseScroll',whell);
}
function whell(ev){
let o = true;
// console.log(ev.wheelDelta);
if(ev.wheelDelta){ //是chrome
o = ev.wheelDelta > 0?true:false;
}else{
o = ev.detail < 0?true:false;
}
//回调函数
fn && fn(o);
}
}
获得当前页面的id,通过这个id向上找pid,直到找到-1为止
比如:周杰伦 id:3 pid:2 传入的是3 data[3].pid -> 2 data[2].pid -> 0 data[0].pid -> -1
1.先找到一个父级
function getParent(id){
//有这个数据并且pid不等于-1
if(data[id] && data[id].pid != -1){
return data[id].pid;
}else{
return null;
}
}
2.获取一堆的父级 //有问题
function getParents(id){ //3
let parent = getParent(id); // 我的音乐
let arr = [];
arr.push(data[id]); //周杰伦 [周杰伦]
while(parent){
arr.unshift(parent); //[微云,我的音乐,周杰伦]
parent = getParent(data[id].pid);
//data[3].pid -> 2
parent = getParent(2) //微云
}
return arr;
}
getParents(3)
function getParents(id){ //3
let arr = [];
let now = data[id]; // data[3] -> 周杰伦
while(now){
arr.unshift(now); //[我的音乐,周杰伦]
now = getParent(now.id); //3
// now = {id:2,我的音乐}
getParent(2);
// now = {id:0,微云}
}
}
如果传入一个0,拿到0和0的子级
子级是否有子级,如果有子级添加tree-title tree-ico close,如果没有就添加tree-title tree-ico-none
第一个是open
点击确定的时候,判断选中的id,是否在移动到的数据或者数据的子孙级上,如果是就不能让其移动
核心思想就是把选中数据的pid换成移动到数据的id
//当点击的时候获取移动到的id
let id = li.dataset.id * 1;
let ary = getChild(globalId);
let arr = ary.filter(item=>item.checked);
let len = arr.length;
if(len < 1){
return;
}
//[{id:0},{id:1}]
//[{id:0},{id:2,pid:0},{id:1}]
let onoff = false;
function fn(id){
let arr = getChild(id);
if(arr && arr.length){
arr.forEach(e=>{
if(id === e.id){
onoff = true;
return;
}else{
fn(e.id)
}
});
}
}
fn(globalId);
if(onoff){
//移动不合法
}else{
//开始移动
}
let ary = getChild(选中的id);
ary.push(data[id]);
ary.forEach(e=>{
if(e.id === id){
onoff = true;
return;
}
})
if(onoff){
//移动不合法
}else{
//开始移动
}
函数嵌套函数,子函数用父函数的参数,通常子函数自己还有参数,
父函数返回子函数,子函数的运行结果还要和父函数的参数有关联并且子函数被外界引用。
function fn(x){
return function(y){
console.log(x,y)
}
}
let f = fn('number')
let f2 = fn('string');
fn('number',5);
fn = (a) => {
return (b)=>{
return a+b;
}
}
fn = a=>b=>a+b
f(5);
f(5);
fn('string',5);
f2('哈哈')
需求:
1.改变this
2.要传参
3.要new
*/
//context 就是改变this的参数
//Function.prototype.bind() fn.bind()
//当需要在bind上写多个参数的时候,就需要剩余运算符,arg除了第一个参数以外的参数
Function.prototype.bind = function(context,...arg){
// console.log(this); //实例fn
let that = this;
function bound(...args){
// console.log(arg);
// that.call(context,...arg);
//合并数组arg.concat(args)
//如果是构造函数this就不能变
//检测this是不是bound构造出来的
//如果是就说明new了bound
if(this instanceof bound){
that.apply(this,[...arg,...args]);
}else{
that.apply(context,[...arg,...args]);
}
// console.log(this);
}
//要把fn的prototype下的方法给bound
if(this.prototype){
bound.prototype = this.prototype;
}
return bound;
}
function fn(a,b,c,d,e){
// console.log(this,a,b,c,d,e);
this.a = a;
console.log(this.aaa);
}
fn.prototype.aaa = 20;
//fn{a:1}
// fn();
let f = fn.bind(document,1,2,3,4);
console.log(new f(5));
1.引入jquery
2.选择器
“#” $('#')
“.”点
标签选择器
属性选择器 input[type=”text”]
3.操作属性
innerHTML : html() html('<li></li>')
innerText : text()
value : val()
getAttribute|setAttribute|removeAttribute:attr(id,’box’)
removeAttr()
className addClass removeClass replaceClass
style : css(‘width’)
cssText : css(‘height’,’200px’)
prop 复选框使用
index(‘设置范围’)
在JQ中一般都有这么一个特性,传一个字符串或者不传参就是获取
两个参数就是设置css(‘height’,’200px’)
如果传入一个参数,这个参数还是对象,批量设置
原生对象转JQ对象 -> $(原生对象)
jQ对象转原生 -> (“img”).get(0);
$ 就是jquery对象
divs.eq(数字) div中的第某个元素,是个jquery对象
index(‘缩小范围’) 自动去找某个元素基于父级的子级为第几个元素
//给所有按钮加点击事件函数
btns.click(function(){
//当点击某个按钮的时候把所有的active清空
btns.removeClass('active');
//把所有div中的show清空
divs.removeClass('show');
//当前按钮的class名变成active
$(this).addClass('active');
//让div中个对应按钮索引的div的className换成show
divs.eq($(this).index('button')).addClass('show');
});
// btns.click(function(){
// $(this).addClass('active').siblings().removeClass('active');
// divs.eq($(this).index()).addClass('show').siblings().removeClass('show');
// });
prop 复选框使用
$('li').each(function(i,ele){ })
all.onclick = function(){
$('input').prop('checked',true);
}
no.onclick = function(){
$('input').prop('checked',false);
}
reverse.onclick = function(){
$('input').each(function(i,ele){ //ele是原生对象
// console.log(ele)
//获取当前input的checked,返回布尔值
if( $(ele).prop('checked') ){
$(ele).prop('checked',false);
}else{
$(ele).prop('checked',true);
}
});
}
不带min为学习版,可以去看里面的代码
http://jquery.cuishifeng.cn/jQuery_selector_context.html
匿名函数自执行,为了不让外界的代码影响到里面的代码(函数内的代码不受外界干扰),但是,这样做外界就拿不到里面的函数了,所以jQ做了一件事,把jQuery挂在了window下,外界就能使用,return把变量挂全局
无new化操作,不在外面new,在里面new
不new自己,new了个其他函数,这个其他函数又拥有,jquery函数原型下的所有方法
链式调用
$('li').css('width','200px').css('height','200px');
(function(global,factory){
factory(global);
/*
判断是否为window环境下,如果不是undefined那么当前
环境就是window,否则为this
*/
})(typeof window !== 'undefined'?window:this,function(global,noGlobal){
//noGlobal 为undefined,因为在低版本下undefined会被修改,函数不传参一定是undefined
/*
如果自己调用自己,就递归了
如何才能让其不递归呢?
1.不自己调用自己就不会递归
2.还能使用jQuery原型上的方法
{
0:xxx,
1:xxx,
length:2
}
*/
function jQuery(selector){
return new jQuery.fn(selector);
}
jQuery.fn = function(selector){
let ele = document.querySelectorAll(selector);
// console.log(ele);
//循环获取的元素,把每个元素挂在实例上
for(let i=0;i<ele.length;i++){
this[i] = ele[i];
}
this.length = ele.length;
}
jQuery.prototype.trim = function(str){
return str.replace(/^\s+|\s+$/g,'');
}
jQuery.prototype.css = function(){
let arg = Array.prototype.slice.call(arguments);
//如果参数为2个,就是设置,有可能是批量设置
if(arg.length === 2){
//循环this的每一项
for(let i=0;i<this.length;i++){
//把每项的样式设置成参数值即可
this[i].style[arg[0]] = arg[1];
}
}
return this;
console.log('进了css');
}
/*
让 jQuery.fn.prototype 等于 jQuery.prototype
当new jQuery.fn 这个实例时上面就有 jQuery原型下的所有方法
*/
jQuery.fn.prototype = jQuery.prototype;
// 把jQuery暴露到了全局
global.jQuery = global.$ = jQuery;
});
var jQuery = 10;
// var lis = new $('li'); //$是个函数名
var lis = $('li'); //不想在外面new
lis.css('background','red').css('width','200px');
append 后添加
remove
prepend(content) 前添加
sibilings(‘设置范围’) 兄弟
parent()
children
$('<li></li>') 添加
$('#txt').keyup(function(ev){
if(ev.keyCode === 13){
let val = $('#txt')[0].value;
// $('#ul').append($('<li>'+val+'</li>'));
$('#ul').prepend($('<li>'+val+'</li>'));
$('#txt').val('');
}
});
jq中所有的事件都为事件绑定
click,mousedown… 都有on()二次封装
on(‘不带On的事件名’,’事件源元素’,事件函数) 事件委托
解绑为off(‘click.aa’)
jq中的ev是二次封装的对象
ev.originalEvent 原生事件对象
事件的冒泡和事件的阻止默认行为:return false;
trigger()
delegate 的原理
事件委托 ——> $('ul').on('click','li',function(){})
// $('li').on('click',function(){
// $(this).css('background','red');
// });
// $('ul').on('click','li',function(){
// $(this).css('background','red');
// })
$('#btn').on('click',function(){
alert(1);
});
//在移入box的时候,让btn的点击事件调用
$('#box').mouseover(function(){
$('#btn').trigger('click');
});
$('#box').on('mouseover',function(ev){
console.log(ev);
$(this).css('background','red');
//在绑定click之前,解除上次click,触发的时候始终就一个
$('button').off('click.bbb');
$('button').on('click.bbb',function(){
console.log('请求');
});
$('button').on('click.aaa',function(){
console.log('请求2');
});
})
$('#box').on('mouseout',function(){
$(this).css('background','#000');
});
工具方法:$.
$.ajax()
$.each()
$.isArray()
不只JQ对象能使用,还可以处理一些原生的数据
工具方法扩展:
$.extend({})
功能方法: $(‘#box’).css();
只能是jQ对象才能使用
$.fn.extend({})
$.extend();
1.浅拷贝 -> Object.assign()
2.深拷贝 -> deepClone
3.插件接口
$(‘#box’).animate({}) 所有的运动都是animate二次封装的
显示隐藏
show() block
hide() none
toggle
改变高度
slideDown
slideUp
slideToggle
淡入淡出
fadeIn
fadeOut
fadeToggle
// $('#box').animate({
// width:100,
// height:200,
// fontSize:50
// },2000,function(){
// console.log('玩完');
// });
$(document).click(function(){
// $('#box').hide(1000);
// $('#box').slideUp(200);
$('#box').fadeOut(200);
});
订阅就是一个收集器,收集事件名和函数,当某个条件成立的时候,批量执行函数
订阅模式
document.addEventListener('click',fn);
发布模式:真的点击document
当某个条件成立的时候发布
document.dispatch(ev);
let event = document.createEvent('HTMLEvents') 创建一个事件对象
//初始化,事件类型,是否冒泡,是否阻止浏览器的默认行为
event.initEvent('事件名',false,false) 初始化事件
element.dispatchEvent(event) 发布
把js中的数据存储到一个自定义的名字中,可以为一个数组也可以为一个对象。
这个名字可变
目的:为了复用
变量数组中是可以设置默认值,用=设置
什么时候会用到默认值呢?
1.赋值没数据 let [a=1] = []; 1
2.赋值为undefined的时候
[] 数组 下标
[任意一个]
[1,2,3][0]=>1
let [a,b] = [1,2];
let [a,b,c] = [1,2];
c = ? undefined
let [,,c] = [1,2,3]
// let arr = [1,2,3,[5,6,[7,[[[[[9]]]]]]]];
let [a,b,c,[d,e,[f,[[[[[g]]]]]]]] = [1,2,3,[5,6,[7,[[[[[9]]]]]]]];
console.log(g);
// console.log(arr[3][2][1][0][0][0][0][0]);
下面的a和b都是对象的属性名
let {a,b} = {
a:1,
b:2
}
c直接代表了a,此时访问a是会报错的,因为a的值已经给了c
访问c其实就是访问a了,改名字使用:号
let {a:c,b:d} = {
a:1,
b:2
}
let {width:w,height:h,fontSize:fz} = getComputedStyle(body);
// console.log(w,h,fz);
// console.dir(console);
let {log,dir} = console;
// log(1);
dir(Array);
在ES6中添加了别的数据结构(Set,Map),他们都需要循环,所以专门通过for of来遍历这些数据结构(循环的统一标准)
let arr = [1,2,3,4];
for(let val of arr){
console.log(val); //1,2,3,4
}
如果使用for of循环的时候要获取数据的键值,那么可以借助数组的keys() 获取键 ,values() 获取值,entries() 获取键值对
这三个方法的返回值都是一个Iterator对象,可以通过next()去调用继续执行
let arr = [1,2,3,4];
for(let [key,val] of arr.entries()){
console.log(key,val); //key为下标,val为值
}
对象默认不能进行for of循环,因为没有遍历器。
以下代码会报错
let obj = {
name:'林同贺',
age:81
}
for(let attr of obj){
console.log(attr);
}
ES6 提供了新的数据结构 Set。它类似于数组,
但是成员的值都是唯一的,没有重复的值
size就等同于length
add(value):添加某个值,返回 Set 结构本身。
delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
has(value):返回一个布尔值,表示该值是否为Set的成员。
clear():清除所有成员,没有返回值。
let s = new Set(); //set{}
s.add('1'); //添加一个数据 //set{'1'}
let s = new Set([1,2,3,4,5,5]); //set{1,2,3,4,5}
写出一个方法join([1,2,3,4],[2,3,5,6])找出交集 //[2,3]
function join(a,b){
let s = new Set(a);
return b.filter(e=>s.has(e));
}
console.log(join([1,2,3,4],[2,3,5,6])); //[2,3]
为了解决这个问题,ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适。
let m = new Map();
m.set(ele,’哈哈’);
console.log(m.get(ele));
当使用for of的时候,内部会调用next方法,只要next的返回值done为false就一直调用next,直到为true就不调用next方法了
let obj = {
name:'林同贺',
age:81
}
/*
遍历接口返回一个对象,对象中有一个next方法
这个方法必须return对象,在这个对象下有2个属性
value,done
value就是for of中的attr
done:为true的时候不遍历
done:为false的时候遍历
*/
obj[Symbol.iterator] = function(){
let keys = Object.keys(obj);//['name','age']
let len = keys.length; //2
let i = 0;
return {
next(){
if(i<len){
return { value:{key:keys[i],val:obj[keys[i++]]}, done: false };
}else{
return {done: true };
}
}
}
}
let iter = obj[Symbol.iterator]();
console.log(iter);
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());
执行顺序:
入栈 -> 主线程执行完成之后 -> 走队列 -> 微任务 -> 宏任务
promise解决把异步编程转成同步编程的,也就是让代码从上往下阅读即可。
有三种状态,pending(初始化,等待)、fulfilled(成功)、rejected(失败)
一旦状态发生改变,状态就会凝固,不能改变
怎么用?
只要是嵌套多个回调函数就可以使用promise进行异步请求的时候使用 fetch
let p = new Promise((resolve,reject)=>{
//resolve:成功;reject:失败
resolve(); /*调用resolve()或reject()函数,是为了触发p.then里的resolve()或reject()函数,这两个地方的函数并不是同一个*/
})
p.then(()=>{//then里面的代码默认是同步的
执行这句话,resolve一定调用了
});
let p = new Promise((resolve,reject)=>{
//放异步代码
//当异步代码执行完成手动调用resolve或者reject
})
返回值为一个promise对象
在promise对象下有几个方法
.then(fn1,fn2)
fn1代表异步代码执行成功之后的回调
fn2代表异步代码执行失败之后的回调
.catch(异步代码执行失败之后的回调)
let p1 = new Promise(()=>{})
let p2 = new Promise(()=>{})
let p3 = new Promise(()=>{})
Promise.all([p1,p2,p3])
(1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
(2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
代码如下:
let p = new Promise((resolve,reject)=>{
resolve(1);
reject(2);
});
p.then((val)=>{
console.log(val)
},(val2)=>{
console.log(val2)
})
/*
Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))
console.log(Promise.resolve())
new Promise(resolve => resolve())
then方法一定是在Promise对象下的。
如果说then有return, 如果return后是promise对象
那么再then,是基于return后这个promise对象的
如果return后并不是promise对象,那么promise对象
往上找,
如果下面还有then,成功的结果为上面then中return的值
注意:
then中的return不能返回Promise实例,不然会报错(死循环)
*/
// Promise.resolve()
// .then(() => {
// console.log('a');
// // throw new Error('error');
// return new Error('error');
// })
// .then((res)=>{
// console.log('b');
// console.log('then:',res);
// })
// .catch((err) =>{
// console.log('c');
// console.log('catch:',err);
// });
promise 源码:
class MyPromise {
constructor(exception){
this.state = 'pending'; //等待状态
this.value;
this.reason;
//记录,订阅成功或者失败的函数
this.fulfilledCallbacks = [];
this.rejectedCallbacks = [];
//成功执行的函数
let resolve = (value) => {
//如果状态是为pending才能执行成功的代码,保证从pending到某个状态,状态是凝固的
if(this.state === 'pending'){
// console.log('一秒之后');
this.state = 'fulfilled'; //只要是成功就把状态变为fulfilled
this.value = value;
this.fulfilledCallbacks.forEach(fn=>fn());
}
}
//失败执行的函数
let reject = (reason) => {
//如果状态是为pending才能执行失败的代码,保证从pending到某个状态,状态是凝固的
if(this.state === 'pending'){
this.state = 'rejected';//只要是失败就把状态变为rejected
this.reason = reason;
this.rejectedCallbacks.forEach(fn=>fn());
}
}
//promise调用的函数
//如果new Promise内的函数为报错,那么直接进reject
try {
exception(resolve,reject);
// console.log(this.state);
} catch (error) {
reject(error);
}
// console.log(this.state)
}
then(onfulfilled,onrejected){
let p2;
p2 = new MyPromise((resolve,reject)=>{
//成功就调用第一个参数,并且把值(this.value||this.reason)传到函数中
if(this.state === 'fulfilled'){
try {
let x = onfulfilled(this.value);
resolve(x);
} catch (error) {
reject(error);
}
}
if(this.state === 'rejected'){
try {
let x = onrejected(this.reason);
reject(x);
} catch (error) {
reject(error);
}
}
if(this.state === 'pending'){
this.fulfilledCallbacks.push(()=>{
try {
let x = onfulfilled(this.value);
resolve(x);
} catch (error) {
reject(error);
}
});
// console.log(this.fulfilledCallbacks)
this.rejectedCallbacks.push(()=>{
try {
let x = onrejected(this.reason);
reject(x);
} catch (error) {
reject(error);
}
});
}
})
return p2;
}
}
new MyPromise(function(resolve,reject){
// throw Error('123');
setTimeout(() => {
resolve(100); //内置的resolve
}, 1000);
// reject(2);
})
.then(function(succ){ //onfulfilled
console.log(succ)
return 200;
})
.then((data)=>{
// console.log(data)
throw Error('错误')
})
.then((d)=>{
console.log(d);
},(error)=>{
console.log('错了',error)
});
1、Array.from(targeObj, mapFunction, mapThis)
生成数组的方法,返回值为一个新数组
从类数组对象、数组、实现了iterator接口的对象生成一个数组
是对原对象的拷贝,且是浅拷贝,意味着修改新的数组元素会影响到原对象
如果数组元素为非对象类型,修改新数组不会影响到原对象
可以接受第二个参数,一个函数,相当于map函数,对每个元素进行相应处理再放入新数组中
第三个参数,指定第二个参数函数内this
2、Array.of(val1, val2, val3, ….)
生成数组的方法,返回一个新的数组
通过将一组数据生成数组
解决Array()由于参数不同产生不同行为的问题
当参数为空时,返回一个空数组
3、 copyWithin(target, start = 0, end = this.length)
在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组
该方法会修改当前数组
结束参数不包括end位置
数组长度不变,数组start到end-1的元素会从target(包括该位置)开始依次覆盖数组原有成员
参数可以是负数,表示倒数
4、find((value, index ,arr) => {})
找出第一个符合条件的数组成员,当回调函数中返回true,则符合条件;如果都没有,则返回undefined
接受第二个参数,用来绑定回调函数的this对象
5、 findIndex
使用与find一致,该方法返回数组成员的位置
6、 fill(value, start, end)
将给定的值,填充数组
第二个参数,指定填充的起始位置(包括该位置)
第三个参数,指定填充的结束位置(不包括该位置)
如果填充的对象是对象,那么赋值的是同一个内存地址的对象,也就是浅拷贝(嗯,浅拷贝,就知道会发生什么事)
7、entries()
用于遍历数组
返回值为数组,里面的元素也是数组,第一元素是下标,第二个是元素值。[[0, ‘a’], [1, ‘b’], [3, ‘c’]]
8、keys()
用于遍历数组
返回数组的键名组成的数组
9、values()
用于遍历数组
返回数组键值组成的数组
10、 includes(start = 0)
返回值一个布尔值,判断数组是否包含给定的值
ES2016引入该方法,类似于字符串的includes方法
第二个参数指定搜索的起始位置,如果是负数,表示倒数位置
相对于indexOf,该方法更加语义化。另外NaN.includes(NaN)返回true,而indexOf不能判断。
可以使用ES5中的some方法实现该功能
11、 flat()
对数组内嵌套的数组“拉平”,就是把数组中的数组的元素挨个拿出来,放数组元素所在位置,返回一个新的数组,不会影响到原来的数组
接收第二个参数,只能拉平的层数,默认只有一层。想无限拉平可以传入Infinity关键字
12、 flatMap(function)
同flat一样,先执行map函数再进行拉平
只能拉平一层
特征一:function关键字与函数名之间有一个星号;
function* fn(){}
特征二:函数体内部使用yield表达式,定义不同的内部状态(yield在英语里的意思就是“产出”)。
function* fn(){
yield 4;
return 5;
}
Generator 函数可以不用yield表达式,这时就变成了一个单纯的暂缓执行函数。
//当前这个fn是一个暂缓执行的函数,一开始是不执行的,只有调用了next方法才执行函数内的代码
function* fn(){
console.log(111);
}
let f = fn();
setTimeout(()=>{
f.next();
},1000)
yield表达式本身没有返回值,或者说总是返回undefined。next方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值。
1.给value设置值 {value:0,done:false}
2.给reset返回了一个undefined
function* numbers () {
yield 1
yield 2
return 3
yield 4
}
// 扩展运算符
[...numbers()] // [1, 2]
// Array.from 方法
Array.from(numbers()) // [1, 2]
// 解构赋值
let [x, y] = numbers();
x // 1
y // 2
// for...of 循环
for (let n of numbers()) {
console.log(n)
}
// 1
// 2
第一步,协程A开始执行。
第二步,协程A执行到一半,进入暂停,执行权转移到协程B。
第三步,(一段时间后)协程B交还执行权。
第四步,协程A恢复执行。
设计理念:
我们采用两种设计理念来规划我们的网站
渐进增强 (progressive enhancement) :
一开始只构建站点的最少特性,然后不断的针对浏览器追加功能
优雅降级 (graceful degradation) :
一开始就构建站点的完整功能,然后针对浏览器测试和修复
块:(独占一行,自上而下排列)
div,ul,li,p,h1--h6,dl dt dd table form
行内元素(内联元素):(不独占一行,不能设置宽高)
span a label em i b strong br
行内块元素:(既能设置宽高又不能独占一行)
input img textarea
块元素转行内元素:
display:inline
行内元素转块元素:
display:block
元素转行内块元素:
display:inline-block
margin
外边距
重叠:
2个元素都加margin值重叠的时候会取最大值
塌陷:
一个父元素里面嵌套了一个子元素,给子元素加margin-top值,想让子元素和父元素有一个距离,但是子元素没有与父元素分离,反而是子元素把margin-top值传递给了父元素,一起出现了下移。
解决方案:
1.加有值的border
2.给父级加上 overflow: hidden;
padding
内边距
float
解决浮动问题:
1. 给父级加高度,但是有个弊端就是,子级占的高度不能超出父级高度
2. 给最后一个浮动元素下添加一个空的元素,并且这个元素的样式为
clear: both;
3.overflow:hidden;
4.添加伪元素
.c::after{
content: "";
clear: both;
display: block;
}
header : 头部
section : 块
main : 主体部分
footer : 底部
nav : 导航;
figure : 图片
figurecaption : 图片描述
article : 文章
mark : 高亮;
canvas : 走显卡,要研究深入,需要算法,位图
WEBGL ——> three.js
音频:
<audio src="https://www.runoob.com/try/demo_source/horse.mp3">
Media.currentTime = value; //当前播放的位置,赋值可改变位置 20
Media.startTime; //一般为0,如果为流媒体或者不从0开始的资源,则不为0
Media.duration; //当前资源长度流返回无限
Media.paused; //是否暂停
Media.defaultPlaybackRate = value;//默认的回放速度,可以设置
Media.playbackRate = value;//当前播放速度,设置后马上改变
Media.played; //返回已经播放的区域,TimeRanges,关于此对象见下文
Media.seekable; //返回可以seek的区域 TimeRanges
Media.ended; //是否结束
Media.autoPlay; //是否自动播放
Media.loop; //是否循环播放
Media.play(); //播放
Media.pause(); //暂停
视频:
<video width="320" height="240" controls>
<source src="movie.mp4" type="video/mp4">
<source src="movie.ogg" type="video/ogg">
您的浏览器不支持Video标签。
</video>
video这个对象下有play播放,pause暂停;
比如:myVideo.play();
controls:显示控制器,播放、暂停、进度条
autoplay 自动播放
// 新增input的属性;
当改变页面中某个元素的边框、背景(非几何发生变化),浏览器会重新对该元素进行渲染,会消耗性能
当修改某个元素的位置或者添加插入、删除某个(些)元素的时候(几何发生变化),页面会进行重新渲染(局部、整个页面),所以操作DOM非常影响性能
整个页面呈现,需要2大部分渲染,DOM渲染,CSS渲染
DOM渲染 ->
DOM树 -> 结构
CSS渲染
CSSOM
渲染树(DOM树 + CSS树) -> 结构应该在哪个位置
linear-gradient 直线渐变
linear-gradient([<起点> || <角度>,]? <点>, <点>…)
只能用在背景上
颜色是沿着一条直线轴变化
参数
起点:从什么方向开始渐变
left、top、left top
角度:从什么角度开始渐变
xxx deg的形式
点:渐变点的颜色和位置
red 50%,位置可选
重复线性渐变
radial-gradient 径向渐变
radial-gradient([[ || ] [at ]?,| at ,]?[,]+);
从“一个点”向多方向颜色渐变
shape形状 : ellipse、circle 或设置水平半径,垂直半径
size:渐变的大小,即渐变到哪里停止,有如下关键词:
closest-side:最近边; farthest-side:最远边;
closest-corner:最近角; farthest-corner:最远角 (默认值)
position :关键词|数值|百分比
重复的径向渐变
background-origin
padding-box 从padding区域显示(默认)
border-box 从box 从content区border区域显示
content-域显示
background-clip
padding-box 从padding区域向外裁剪
border-box 从border区域往外裁剪
content-box 从content区域往外裁剪
background-size
100% 100% 百分比
10px 10px 数值
contain 按原始比例收缩,背景图显示完整,但不一定铺满整个容器
cover 按原始比例收缩,背景图可能显示不完整,但铺满整个容器
background-attachment
背景图片是滚动的还是固定的 fixed(固定的) 默认是滚动的
border-radius: 1-4个数字 / 1-4个数字
前面是水平半径,后面是垂直半径
四个数字方向分别是左上 右上 右下 左下
不给“/”则水平半径和垂直半径一样
border-radius: 10px/5px;
border-radius:60px 40px 30px 20px /30px 20px 10px 5
例子 : 圆 椭圆 半圆 扇形
box-shadow: h v blur spread color inset;
h :水平方向偏移
v : 垂直方向偏移
blur : 模糊半径
spread : 扩展半径
color : 颜色
inset :加上这个表示内阴影 默认是外阴影
text-shadow : x y blur color
x轴偏移 y轴偏移 模糊度 颜色
多层阴影制作文字立体效果 ,设置多种颜色,中间以逗号隔开
text-stroke: 2px blue 文字添加边框
通过设定1px的透明边框,可以让文字变得平滑
颜色设成透明能创建镂空字体
结构选择器
:nth-child(n)
:nth-child(2n) 偶数元素
:nth-child(2n+1) 奇数元素
:nth-of-type(n)
:first-child
:last-child
:only-child
:only-of-type
:empty
否定选择器
:not()
属性选择器
E[attr=val]
E[attr|=val] 只能等于val 或只能以val-开头
E[attr*=val] 包含val字符串
E[attr~=val] 属性值有多个,其中有一个是val
E[attr^=val] 以val开头,不管后面是什么
E[attr$=val] 以val结尾,不管前面是什么
目标伪类选择器
:target 用来匹配url指向的目标元素
存在url指向该匹配元素时,样式效果才会生效
伪元素
: first-line 匹配第一行文本
: first-letter 匹配第一个首字符
: before 和 : after DOM元素前后插入额外的内容
transition 过渡动画 加在运动物体身上
transition-property 过渡属性 all | [attr]
transition-duration 过渡时间
transition-delay 延迟时间
transition-timing-function 运动类型
如果transition第一个参数不写,默认为all(所有的方向)
也可以指定,如果为指定只有指定的属性才能享受到运动
连写:
transition:运动属性 运动时间 运动的延迟
小技巧:
在使用css3动画的时候,有可能会出现运动失效的情况,
可以通过定时器setTimeout,让运动进行异步运行,可以解决BUG
box.style.display = 'block';
setTimeout(() => {
box.style.transition = '1s';
box.style.width = '300px';
box.style.height = '300px';
box.style.opacity = .3;
});
transitionend webkitTransitionEnd mozTransitionEnd….
ele.addEventListener(‘webkitTransitionEnd’,function(){},false);注意:如果运动的属性有多个,那么每个运动完成时都会触发一次TransitionEnd,就可能导致在同一时间内执行了多次代码
transform是可以连写的,但是在写的过程当中要注意先后顺序,不然会发生意想不到的惊喜。
transform: rotate(-80deg) translateX(300px) ; 先执行角度再执行位移
transform: translateX(300px) rotate(-80deg);
rotate() 旋转函数
deg 度数
transform-origin 旋转的基点
默认为center center
可以设置left top bottom right
skew() 倾斜函数 deg
skewX()
skewY()
scale() 缩放函数 默认值是1
scaleX()
scaleY()
translate() 位移函数
translateX() left
translateY() top
transform-style : flat | preserve-3d (3D空间展示) 内容为3D
transform:perspective(800px) 景深 近大远小
@keyframes 自定义的名字 {
0%{}
100%{}
}animation: 自定义的名字 1s
布局 -> 适配 -> REM适配
响应式布局
苹果 ios object-c swift 闭源
需要APPstore审批,时间大概在7天左右,如果没有审核过又要等7天左右,当真正上线的时候有可能已经赚不到钱了
安卓 c, 应用就是用java开发的 开源
之前解决跨端的问题,还是比较复杂
前端的移动端
套web,用浏览器套写好的html,打包好,用户去下载打包后的应用,打开应用就等同于打开浏览器
弊端:使用不了手机系统上自带的功能,摄像头、相册….
专门在原生应用上进行开发,把安卓和苹果的接口封装好暴露出去给js去调用
微信JSSDK
react-native
Hybrid
REM适配:
em 单位 这个单位被自己或者父级的字体大小影响,最小为8px
rem 单位 root(根,html) em
<script>
var desW = 640; //设计图
function refreshRem() {
let doc = document.documentElement; //html
// alert(doc.nodeName);
let winW = doc.clientWidth;
//设计图 / 可视区
let radio = desW / winW; //开发模拟器也要640的位置上
if (winW >= desW) {
//100px为了好计算 100px -> 1rem 55px -> 0.55rem
doc.style.fontSize = '100px';
return;
}
doc.style.fontSize = 100 / radio + 'px';
}
refreshRem();
window.addEventListener('resize', refreshRem);
</script>
Less 是一门 CSS 预处理语言
写css更加方便(效率提高),好维护,比较规范
属性变量
@w:width; 定义
@{w}:100px; 使用
传参
定义:
.变量名(@1:也可以定义默认值,@2){
border:@x solid @c;
}
调用:
.变量名(加实参);
ontouchstart 手指按下
ontouchmove 手指移动
ontouchend 手指抬起
一个目标上按下、移动、抬起是一套事件,按下在哪个元素身上,当抬起的时候就算不在按下的元素身上,触发抬起也是按下的那个元素;所以不用嵌套绑定事件函数。
let event = document.createEvent('HTMLEvents');
event.initEvent("top", false, false);
let event2 = document.createEvent('HTMLEvents');
event2.initEvent("bottom", false, false);
document.addEventListener('touchstart',start);
document.addEventListener('touchmove',move);
document.addEventListener('touchend',end);
document.addEventListener('top',function(){
console.log('上划')
});
document.addEventListener('bottom',function(){
console.log('下划')
});
1、 touches:当前位于屏幕上的所有手指触摸点的一个列表。
touches(作用于整个屏幕):表示当前跟踪的触摸操作的Touch对象的数组–点击触摸时有多少个Touch对象
2.、targetTouches:当前元素对象上所有触摸点的列表。
targetTouches(作用于一开始start的元素):特定于事件目标的Touch对象的数组–点击触摸时有多少个Touch对象
3、 changedTouches:涉及当前事件的触摸点的列表。
√ changedTouches(一上来触摸的元素,并且不会累计手指数量):表示自上次触摸以来发生了什么改变的Touch对象的数组–发生改变的活着当前是多少touch对象,即相当于touchmove
let disX = 0;
let disY = 0;
function start(ev){
disX = ev.changedTouches[0].pageX;
disY = ev.changedTouches[0].pageY;
}
function move(){
}
function end(ev){
let endX = ev.changedTouches[0].pageX;
let endY = ev.changedTouches[0].pageY;
let X = endX - disX;
let Y = endY - disY;
if ( Math.abs(X) > Math.abs(Y) && X > 0 ) {
alert("像右滑动");
}else if ( Math.abs(X) > Math.abs(Y) && X < 0 ) {
alert("像左滑动");
}else if ( Math.abs(Y) > Math.abs(X) && Y > 0) {
document.dispatchEvent(event2);
}else if ( Math.abs(Y) > Math.abs(X) && Y < 0 ) {
// alert("上滑动");
document.dispatchEvent(event);
}else{
alert("触摸");
}
}
在移动端开发的时候尽量少用DOM0,都用DOM2
因为DOM0在有些浏览器上调试是不正常的,但是真机上是OK的,所以为了方便开发,移动端的事件都用DOM2绑定
在移动端三事件是比PC的事件快大约300ms左右
苹果之前出了一个用户体验非常好的效果,在屏幕某个位置300ms左右连续点击2次就会触发放大缩小的效果。
在移动端中点击某个物体(盒子),如果盒子下方有焦点(input,a..)元素,那么如果在300ms之内,上方的盒子消失或者偏移,那么就会触发下面的焦点元素行为。
1.不放焦点元素,如果要跳转,使用js操作。
弊端:对seo优化有影响
2.使用焦点元素,javascript:void(0);
3.通过阻止全局的默认行为,阻止焦点元素的行为,然后单另在焦点元素上绑跳转。
4.把消失或者偏移的时间放缓些,尽量超过300ms
action=”url路径”
url路径是后台给你的地址
method = “请求方式”
<!-- get-->
<form action="/get" method="GET">
用户名:<input type="text" name="user"/>
密 码:<input type="password" name="pw"/>
<input type="submit" value="提交">
</form>
<!-- post -->
<form action="/post" method="POST" enctype="application/x-www-form-urlencoded">
用户名:<input type="text" name="user"/>
密 码:<input type="password" name="pw"/>
<input type="submit" value="提交">
</form>
//fetch --- get请求
user.onblur = function(){
fetch('/get?user='+this.value)
.then(d=>d.json())
.then(data=>{
// span.innerHTML = data.msg;
// console.log(data);
});
}
//fetch --- post请求
fetch('/post',{
method:'post',
body:''+new URLSearchParams({
user:this.value //'user='+this.value,
}),
headers:{
'content-type':'application/x-www-form-urlencoded'
}
}).then(d=>d.json())
.then(d=>{
console.log(d);
});
Asynchronous(异步) Javascript(js) And(和) XML(可扩展标记语言)
异步的js和xml前后端数据交互的技术
拿到数据之后做什么,如何处理?
后端的代码是同步执行的,一句话一句话执行,上面的代码执行完才执行下面的代码, 也就是说上面的代码没有执行完会阻塞下面代码的执行,那么这样会导致页面中因为某块数据没有加载完成而影响整个页面的显示
全局刷新(用户体验较差)
js引擎、gui引擎、事件引擎、请求引擎
url:
http://localhost:80/3_ajax.html?page=1#hash=xy;
协议:
http:// 超文本传输协议
file:// 本地文件传输协议
https:// 比http更加安全
ftp: 文件传输协议
域名:ip的别名
localhost
14.215.177.39 baidu
端口:
放在域名后面的
服务器默认端口为80
文件路径:
3_ajax.html
查询信息:
?page=1 & id=0
一个参数与另一个参数用&符分割
hash信息:
#hash=xy
https://www.cnblogs.com/tisikcci/p/5866753.html
http://www.ruanyifeng.com/blog/2012/05/internet_protocol_suite_part_i.html
DNS去解析url找到对应的服务器
通过端口访问对应的服务
http请求
三次握手和四次挥手,要保证有效请求
响应 -> http状态码
gui渲染页面
1.有一部电话
2.输入号码
3.发送(按绿按钮)
4.等待
5.通话
get:(应用场景数据渲染)
url -> 后端给的 '/get?user=yuhaiyang'
xhr.open('get','/s?wd=zhufeng')
缺点:
会通过浏览器的缓存机制,缓存你的url的访问记录(相对不安全),url的长度是有限制的,每个浏览器都不一样,不能传输体积较大的文件,中文需要通过encodeURI转码
优势:
速度快 -> 3步就能请求到数据
//jquery版--get请求
$('#user').blur(function(){
$.ajax({
url:'/get?user='+$(this).val(),
dataType:'json',
success:function(data){
if(data.code === 1){
$('#span').html( data.msg );
}
}
})
});
//ajax---get请求
user.onblur = function(){
var xhr = new XMLHttpRequest;
xhr.open('get','/get?user='+encodeURI(this.value));
xhr.send();
xhr.onload = function(){
var data = JSON.parse(xhr.responseText);
span.innerHTML = data.msg;
}
}
//get请求,是否异步
un.onblur = function(){
img.style.display = 'block';
let xhr = new XMLHttpRequest;
xhr.open('get','/sleep?name='+this.value,true);//是否异步
xhr.timeout = 3000;
xhr.ontimeout = function(){
console.log('超时');
}
xhr.send();
xhr.onprogress = function(ev){
console.log(ev);
}
post:(用户信息,上传视频…)
优势:
参数不会被浏览器缓存,相对安全;请求方式是走的服务器,理论上来说体积可以无限大(一般后台人员会限制);在发送之前需要设置请求头
缺点:
速度相对get要慢。
需要注意的是:
在真实开发中,ajax请求基本上都是用第三方库来请求的
第三方库已经把ajax封装起来了,看不见send
用户名:<input type="text" id="un" name="user"/>
密 码:<input type="password" name="pw"/>
<input type="button" value="提交">
<script>
//ajax---post请求
user.onblur = function(){
var xhr = new XMLHttpRequest;
xhr.open('post','/post');
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
//把传给后端的数据放在send中
xhr.send('user='+this.value);
xhr.onload = function(){
var data = JSON.parse(xhr.responseText);
span.innerHTML = data.msg;
}
}
//post请求,是否异步
un.onblur = function(){
let xhr = new XMLHttpRequest;
xhr.open('post','/post',true);//是否异步
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
xhr.send('user='+this.value);
// xhr.onerror = function(){
// console.log('失败');//一般是传输的时候有问题
// }
xhr.onload = function(){
// console.dir(xhr);
console.log(xhr.responseText);
}
// xhr.onloadend = function(){
// console.dir(2);
// }
console.log(1);
};
</script>
<button id="btn">点击生成</button>
<ul id="ul"></ul>
<script>
//fetch请求
btn.onclick = function(){
fetch('./data.xml')
.then(data=>data.text())
.then(d=>{
let re = new RegExp(`<person>\\s+<name>([\\u4e00-\\u9fa5]+)</name>\\s+<age>(\\d+)</age>\\s+<sex>([\\u4e00-\\u9fa5])</sex>\\s+<info>([\\u4e00-\\u9fa5。?\.\\d,,]+)</info>\\s+</person>\\s+`,'g');
let html = '';
// console.log(d.match(re))
// console.log(d.match(/^<name>[\u4e00-\u9fa5]+<\/name>$/g));
// d.replace(re,($0,$1,$2,$3,$4)=>{
// html += `<li>我的名字叫${$1},芳龄:${$2}性别:${$3}---我想说:${$4}</li>`
// // console.log($1,$2,$3,$4);
// });
// ul.innerHTML = html;
});
}
//ajax---get请求
btn.onclick = function(){
let html = '';
let xhr = new XMLHttpRequest;
xhr.open('get','data.xml');
xhr.send();
xhr.onload = function(){
let d = xhr.responseXML;
let person = d.getElementsByTagName('person');
for(let ele of person){
let arr = [];
for(let i=0;i<4;i++){
let {innerHTML} = ele.children[i];
//在XML中的innerHTML不是都兼容的
arr.push(innerHTML);
}
html += `<li>
我的名字叫${arr[0]},芳龄:${ele.children[1].innerHTML}性别:${ele.children[2].innerHTML}---我想说:${ele.children[3].innerHTML}
</li>`
}
ul.innerHTML = html;
// console.log(person)
// console.log(xhr.responseXML)
}
}
</script>
onerror 请求失败 比如断网
onload 请求成功
onprogress 进度
upload 上传相关
onreadystatechange 监听状态的变化 所有浏览器都兼容
readyState * 状态进行到了哪一步0-4 一共有5步,只是状态值为4的时候说明请求完成,0是监听不到的
0:请求未初始化
1:服务器连接已建立
2:请求已接收
3:请求处理中
4:请求已完成,且响应已就绪
responseText 后端返回的数据
responseXML XML的document对象
status HTTP状态码 ( 比如:200,404)
1-6开头状态码,如下:
1 消息
2 成功 200-207为成功
3 重定向
301 永久重定向
302 请求的资源临时从不同的 URI响应请求。由于这样的重定向是临时的,客户端应当继续向原有地址发送以后的请求。只有在Cache-Control或Expires中进行了指定的情况下,这个响应才是可缓存的
304 如果客户端发送了一个带条件的 GET 请求且该请求已被允许,而文档的内容(自上次访问以来或者根据请求的条件)并没有改变,则服务器应当返回这个状态码。304响应禁止包含消息体,因此始终以消息头后的第一个空行结尾。
305 需要代理
307 请求的资源临时从不同的URI 响应请求
4 请求错误(前端的锅)
400 Bad Request
1、语义有误,当前请求无法被服务器理解。除非进行修改,否则客户端不应该重复提交这个请求。
2、请求参数有误。
401 Unauthorized
403 Forbidden 服务器已经理解请求,但是拒绝执行它
404 请求失败
5、6 服务器错误
只要出现5后端的问题
timeout 超时 设置时间毫秒 比如: xhr.timeout = 3000
ontimeout 超时回调
onreadystatechange 监听状态的变化 所有浏览器都兼容,示例:
事件绑定在send之前,能够监听建立连接,也就是在异步的情况下,能够多监听一步
//**onreadystatechange 监听状态的变化 所有浏览器都兼容**
un.onblur = function(){
//虽然IE6以上浏览器支持XMLHttpRequest对象,但是每个版本会有差距
var xhr = new XMLHttpRequest;
xhr.open('get','/get1?user='+this.value,true);//是否异步
//事件绑定在send之前,能够监听建立连接,也就是在异步的情况下,能够多监听一步
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
if(xhr.status>=200 && xhr.status <= 207 || xhr.status == 304){
console.log('成功');
}else{
console.log('请求失败');
}
// console.log(xhr.readyState);
// console.log(xhr.status);
}
// console.log(xhr.readyState);
}
xhr.send();
console.log(1);
// xhr.onload = function(){
// console.log(xhr.responseText);
// }
};
// console.log(Array.prototype.push);
注意:
axios不能使用jsonp,如果公司用jsonp跨域,
1、要么用我们封装的;
2、要么自己封装;
3、要么下载一个叫做fetchjsonp的工具(npm i fetchjsonp)
;(function(global,factoy){
factoy(global);
})(this,function(global,noGlobal){
function Axios(json){
return new axios(json);
}
Axios.prototype = {
constructor:axios,
get:function(url,data){
return new Promise((resolve,reject)=>{
this.xhr.open('get',url);
this.xhr.onreadystatechange = this.ready.bind(this,resolve,reject);
this.xhr.send(this.quryString(data));
});
},
post:function(url,data){
return new Promise((resolve,reject)=>{
this.xhr.open('post',url);
this.xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
this.xhr.onreadystatechange = this.ready.bind(this,resolve,reject);
this.xhr.send(this.quryString(data));
});
},
//把对象变成字符串以key=val&key2=val2...
quryString:function(data){
var arr = [];
for(var attr in data){
arr.push(attr +'='+ data[attr]);
}
return arr.join('&');
},
all:function(ary){
return Promise.all(ary);
},
ready:function(resolve,reject){
if(this.xhr.readyState === 4){
if(this.xhr.status >= 200 && this.xhr.status <=207 || this.xhr.status === 304){
resolve(this.xhr.responseText);
}else{
reject(this.xhr.status);
}
}
}
}
function axios(json){
this.xhr = new XMLHttpRequest;
}
axios.prototype = Axios.prototype;
global.axios = Axios();
});
axios({
method: 'post',
url: '/user/12345',
data: {
firstName: 'Fred',
lastName: 'Flintstone'
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
ajax和axios、fetch的区别
https://www.jianshu.com/p/8bc48f8fde75
一种浏览器缓存,但是它起源又是为了解决后台存储的,cookie是一个后端技术。不过它还可以是前端的技术
常用的猜你喜欢、刷新登录、7天免登录…
当用户登录成功之后,再次刷新页面,页面还是会显示让用户再次登录,默认服务器不会保留用户的登录状态
当用户登录之后,服务器给浏览器设置了一个名为cookie的一种浏览器缓存(切换浏览器之前在另一个浏览器上设置的cookie就无效了),再次刷新的时候浏览器会携带一个cookie码传给服务器,服务器通过这个cookie值进行验证等操作就能判断用户是否登录过。
1.读 document.cookie; 如果有多个值,那么一对存储值和另一对存储值由; (分号+空格)分割
2.写 document.cookie = ‘key=value’ ** 如果存储值为对象,那么会默认调用toString方法
1.cookie在每个域名中的体积,根据浏览器的规则来定,浏览器的品牌不一样,个数就不一样(对于高版本浏览器据说有5M)
Microsoft指出InternetExplorer8增加cookie限制为每个域名50个,但IE7似乎也允许每个域名50个cookie。
Firefox每个域名cookie限制为50个。
Opera每个域名cookie限制为30个。
Safari/WebKit貌似没有cookie限制。但是如果cookie很多,则会使header大小超过服务器的处理的限制,会导致错误发生。
注:“每个域名cookie限制为20个”将不再正确!
2.生命周期(临时、永久或可控)
(1)默认生命周期为浏览器关闭
(2)Expires=要设置的时间 这样就可以让cookie在指定时间内死亡
new URLSearchParams
''+ new URLSearchParams({user:'tony',pass:123})
体积: 5M左右 ,读写等操作
写:
localStorage.setItem(key,value)
注意:
value -> 会自动转成字符串,用数组或者对象的时候要用JSON.stringify(value)
读:
localStorage.getItem(key);
清除:
localStorage.clear();
生命周期:只要不清除就一直在
window.onstorage 当localStorage的值发生的变化就触发(是兄弟页面触发,自己不触发)
跨域(源)
同源策略:是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。源:域名、端口、协议
同源:同域名、端口、协议
跨域:不同域名|不同端口|不同协议,只要有一个不同就跨域
Access to XMLHttpRequest at ‘http://localhost:8080/kuayu?user=cc’ from origin ‘http://localhost’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.
https://www.taobao.com:80
https://www.daohao.com:80
https://www.taobao.com:80
https://www.taobao.com:81
https://www.taobao.com:80
file://www.taobao.com:80
常见的解决跨域问题(CORS、服务器代理、jsonp)
1.CORS进行跨域
需要高版本浏览器下使用XMLHttpRequest去进行请求
需要服务器端在响应头上设置一个白名单
Access-Control-Allow-Origin:'*'
这种解决方案是个趋势,低版本不兼容
2.服务器代理
A能访问B(同域),但是A不能访问C(跨域),B又能访问C(服务器操作)
A去访问B,就等同于能够访问C
3.jsonp JSON + Padding 内填充的数据
兼容低版本
前后端操作都不复杂
script标签可以尽量去解析js代码,而且支持跨域
1.必须在全局暴露一个函数
function fn(data){
console.log(data);
}
2.服务器传输的数据必须是函数名 + 括号 在括号中填充数据的这种格式
fn([1,2,3,4,5]);
基于V8引擎(谷歌浏览器的引擎)渲染JS的工具或者环境
安装node网址:https://nodejs.org/en/
1.在服务器下千万不要建中文的文件夹
2.运行的时候,使用localhost/xx.html运行,千万不要双击或者直接用编辑器打开(只有把文件放到public目录下才能使用localhost)
3.运行文件的时候,一定要开服务器。
找到当前文件夹按住shift + 鼠标右键 输入node app
把当前文件夹放到vscode中,然后点击感叹号,找到终端 输入node app
json -> '{"name":"于海洋"}' || '[1,2,"3"]'
XML ->
<person>
<name>于海洋</name>
<hobby>跟着倪老师学习js</hobby>
</person>
1、编译性语言
(1)只须编译一次就可以把源代码编译成机器语言,后面的执行无须重新编译,直接使用之前的编译结果就可以;因此其执行的效率比较高;
(2)编译性语言代表:C、C++、Pascal/Object Pascal(Delphi);
(3)程序执行效率比较高,但比较依赖编译器,因此跨平台性差一些;
2、解释性语言
(1)源代码不能直接翻译成机器语言,而是先翻译成中间代码,再由解释器对中间代码进行解释运行;
(2)程序不需要编译,程序在运行时才翻译成机器语言,每执行一次都要翻译一次
源代码—>中间代码—>机器语言
后台管理(vue全家桶、react全家桶) + node + 数据库
Node.js是基于Chrome的V8 JavaScript引擎构建的JavaScript运行环境
单线程
事件驱动
非阻塞I/O
全球最大的包管理平台(下载资源、学习平台)
一个JS模块(所有封装好可以供其他人调取使用的都可以称之为模块或者包)管理的工具,基于npm可以安装下载JS模块,他会生成一个node执行的命令(可以在DOS窗口或者终端命令中执行):node xxx.js
如果不成功,可以找相同电脑配置的人员,把安装成功的NODE文件夹拷贝到自己的电脑上,通过配置环境变量,来实现NODE安装
node的模块化管理
通过require(‘http’)引入文件 如果不写路径说明要么node中,要么在node_modules中,否则要加路径如require(‘./3’)
通过module.exports = {key:value} 去导出
写一个人生中第一个服务器
const http = require('http'); //引包
//创建服务器
/*
request 请求 客户端请求
response 响应 发送给客户端
*/
//创建服务
const app = http.createServer((request,response)=>{
console.log(1);
//发送给客户端
response.write("{code:1,msg:'abc'}");
//结束发送
response.end();
});
//80监听
app.listen(80);
当回调函数中的条件成立的时候返回符合条件的那项;如果找不到,就返回undefined
Array arr.find(function(item,i,all){})
let ary = [1,2,3];
let a=ary.find(e=>e>1)
console.log(a);//=> 2
xxx.com?user=abc&pass=123&cb=fn&wd=警察叔叔
返回传入一个测试条件(函数)符合条件的数组第一个元素位置
当数组中的元素在测试条件时返回 true 时, findIndex() 返回符合条件的元素的索引位置,之后的值不会再调用执行函数。
如果没有符合条件的元素返回 -1
array.findIndex(function(currentValue, index, arr), thisValue)
let arr = [10,20,30];
let aa = arr.findIndex(function (item, i, all) {
return item > 10;
})
console.log(aa); //=>1
到www目录下找1.txt文件
const fs = require(‘fs’);
读文件操作:fs.readFile / fs.readFileSync
const fs = require('fs');
// fs.readFile('./www/1.txt', (error, data) => {
// if (error) {
// console.log(404);
// } else {
// console.log(data.toString());
// }
// });
try {
let data = fs.readFileSync('./www/1.txt');
console.log(data.toString())
} catch (error) {
console.log(404);
}
写入文件操作:fs.writeFile / fs.writeFileSync
const fs = require('fs');
// fs.writeFile('./www/2.txt', 'wojiaosha', (error) => {
// if (error) {
// console.log('失败');
// } else {
// console.log('成功');
// }
// });
try {
fs.writeFileSync('./www/3.txt', 'qwerrtt')
console.log('成功');
}catch(error){
console.log('失败');
}
删除文件操作:fs.unlink / fs.unlinkSync
const fs = require('fs');
// fs.unlink('./www/3.txt', (error) => {
// if (error) {
// console.log('失败');
// } else {
// console.log('成功');
// }
// });
try {
fs.unlinkSync('./www/3.txt')
console.log('成功');
} catch (error) {
console.log('失败');
}
http://www.expressjs.com.cn/
Express 是一个简洁而灵活的 node.js Web应用框架, 提供了一系列强大特性帮助你创建各种 Web 应用,和丰富的 HTTP 工具。使用 Express 可以快速地搭建一个完整功能的网站。
项目初始化
npm init 如果没有特殊情况无脑回车,走默认配置
npm init -y
安装express:npm install express –save
创建一个js文件
引包 const express = require(‘express’)
调用express const app = express()
监听 app.listen(80)
如果是get -> app.get(‘路径’,(request,response)=>{})
request.query 获取参数对象
response.send() response.json() 发送
中间件: 一堆功能函数,能让使用的对象更加强大(让对象拥有功能函数的功能)
需要use去关联 比如:app.use(express.static(‘www’)) 管理静态文件
const bodyParser = require(‘body-parser’);
app.use(bodyParser.urlencoded({ extended: false }));
req.body 获取参数
app.use(‘/user’,require(‘./routers/users’));
users.js的内容如下:
const express = require('express');
const router = express.Router();
router.get('/',function(req,res){ // /user
res.send('{code:0,msg:"/user"}');
});
router.get('/add',function(req,res){
res.send('{code:0,msg:"/user/add"}'); // /add -> /user/add
});
router.get('/rm',function(req,res){
res.send('{code:0,msg:"/user/rm"}');
});
module.exports = router;
安装 npm i swig -S
引包 require(‘swig’);
配置
app.set('views','./www'); www就是你模板放置的路径
app.set('view engine','html');
app.engine('html', swig.renderFile);
指定路由模板
app.use(‘/’,require(‘./routers/index’));
index文件中
router.get('/',(req,res)=>{
res.render('index',{
title:'首页',
data:[1,2,3,4]
})
});
index.html中
输出数据:
{{ 数据的名称 }} 如:{{title}} -> 首页
循环数据:
{%for key,val in data%}
{%endfor%}
判断:
{%if 条件%}
执行语句一
{%else%}
执行语句二
{%endif%}
npm install express-generator -g
express –view=pug myapp
cd myapp
npm install
npm start 启动
懵,不懂,难,恶心,带有人类的逆反心理,难点是英语(报错是英语报错)
多练,多报错多翻译,弄到报错眼熟你就学会了,见过就会没见过就不会
安装、安装、安装,翻文档看配置,进行配置
优化
开发依赖主要是为了维护(给程序员看的)
<script src="1.js">
<script src="2.js">
CommonJS 用到既加载(规范)
模块化开发
CMD seajs
AMD 先加载后使用(规范) require
glup
引入 -> require
导出 -> module.exports
ES6:引入:
import { fn } from '1.js' //'模块名称';
export function fn(){} //1.js
生产依赖主要给用户使用的(体积小)
https://www.webpackjs.com/concepts/ 官网
难点-> 英文
目标:当学完之后,希望大家都能够自己手动配置webpack
一般结果 -> 学完之后,啥东西?干啥的?我是谁?我在哪?
<div id="box"></div> -> <div id=box></div>
入口(entry)
entry:'./2.js', 字符串,单入口
entry:['./2','./1'], //多入口单出口
output:{
filename:'haha.js'
}
//多入口多出口
entry:{
index:'./2',
aa:'./1'
},
output:{
filename:'[name].[hash:8].js'
}
name就表示entry的key
输出(output)
loader:模块的源代码进行转换
module:{
rules:[
{
test:/\.css$/, //处理上面文件
use:[需要的模块]
}
]
}
插件(plugins)
plugins:[new HTML({}),new Xxx()]
npm i webpack webpack-cli –golbal (第一次安装需要那么做)
项目目录名称千万不要写webpack
npm init -y(到你项目的目录中输入,//初始化默认的package.json文件)
npm i webpack webpack-cli –save (只要安装过,就不用安装上面那句话了)
自己手动创建一个webpack.config.js的文件
const path = require('path');
const obj = {
entry:'./2.js',
output:{
filename:'2.js',
path:path.relove(__dirname,'./build')
}
}
module.exports = obj;
找到scripts 设置值为 “build(自定义名称)”:”webpack”
到命令行输入npm run build
production 生产环境 让用户看的
development 开发环境 让程序员看的
1:npm install html-webpack-plugin –save-dev //自动快速的帮我们生成HTML。
2:npm install css-loader style-loader –save-dev//样式文件,我们需要两种loader,css-loader 和 style-loader,css-loader会遍历css文件,找到所有的url(…)并且处理。style-loader会把所有的样式插入到你页面的一个style tag中。
3:npm install babel-loader css-loader style-loader –save-dev// 安装加载器(babel-loader 进行转码,css-loader 对 css 文件进行打包;style-loader 将样式添加进 DOM 中)
4:npm install sass-loader node-sass –save-dev//css预编译程序,还需要添加node-sass来解析sass文件
5:npm install url-loader –save-dev//图片自动转成base64编码的
6:npm install jquery moment –save-dev//添加第三方库(jquery和moment)
7:npm install babel-preset-es2015 –save-dev//添加ES6的支持
8:npm install babel-preset-es2015 babel-preset-react –save-dev//安装转码规则
9:npm install webpack-dev-middleware –save-dev//服务器端使用的是express框架,你还可以直接安装express的middleware,webpack配合express
10: npm install react –save-dev//安装并引用 React 模块
11:npm install react react-dom –save-dev//添加react和react-dom
12:npm install react-hot-loader –save-dev//react-hot-loader 是一款非常好用的 React 热插拔的加载插件,通过它可以实现修改-运行同步的效果,配合 webpack-dev-server 使用更佳!
13:npm install –save-dev autoprefixer postcss-loader –save-dev//
14:npm install babel-loader coffee-loader –save-dev//
15: npm install autoprefixer-loader –save-dev//
16: npm install vue –save-dev//
17: npm install -d //将项目中package.json依赖的包全部下载到该项目中
npm i webpack-dev-server -D
配置
devServer: {
clientLogLevel: 'warning',
hot: true,
compress: true,
host: 'localhost',
port: 80,
open:true,
proxy: { //服务器跨域
'/api': {
target: 'http://www.yijianbaoshui.com/acc/queryAll.do',
// changeOrigin: true,
pathRewrite: {
'^/api': ''
}
}
}
},
//前端只需要请求/api这个接口就等同于访问http://www.yijianbaoshui.com/acc/queryAll.do
例子如下:
fetch('/api?orgid=bc97556b69a34ec2b885c6db0eab9acf&orgName=&loginType=0&curRoleId=2&groupId=811fa6641f974b9ab2855268ff59b27f')
.then(e=>e.json())
.then(d=>{
console.log(d);
})
export function fn(){
}
import {fn} from './xx';
export default fn(){
}
import fn from './xx';
export * from './xx';
import {} from './xx';
as能改名字
import * as obj from './xx';
console.log(obj.fn);
angular 谷歌 粘性很强,强主张 TS-> typeScript,脏值检查
react facebook 虚拟DOM,让开发者专注于数据处理,并且根据数据的变化去操作DOM(性能优化的去操作),生态(手机端)
用于构建用户界面的 JavaScript 库,react是V层框架,专注于视图层
M:Model 数据
V:View 视图
C:Controller 控制器
用对象去模拟DOM树
createElement('li',{className:'active',id:'box',children:[
]},'哈哈')
{
tagName:'li',
attr:{
className:'active',
id:'box',
children:[
{
tagName:'div',
attr:{id:'div1'},
text:'呵呵'
}
]
}
text:'哈哈'
}
npm install -g create-react-app //全局下载安装官方提供的脚手架
create-react-app 文件名 //本地直接引用,不用npxnpx create-react-app myapp
cd 文件名
npm start
or
yarn 安装
在npm中安装 : npm install -g yarn
import React from 'react';
import ReactDOM from 'react-dom';
ReactDOM.render(document.getElementById('root'));
ReactDOM.render() 有三个参数
1.结构
2.根元素,默认为root
3.渲染完之后的回调函数
注意:
结构的顶层只能有1个元素
比如:
<div>你好</div><div>haha</div> 报错
<div>
<div>你好</div><div>haha</div> 对的
</div>
不用jsx的语法
const H1 = React.createElement(
'h1',
{className: 'greeting'},
'Hello, world!'
);
用jsx的语法
<h1 className="greeting">Hello, world</h1>
class必须写成className
标签必须是闭合状态 包括单标签 <input />
{ }
可以执行js语句
如果花括号内部有数组,默认展开
如果元素属性是一个变量需要花括号 src={obj.xx}
受控组件、非受控组件
箭头函数中用{ }就必须写return
事件
onClick、onMouseOver...
onClick = {()=>{
事件干的事情
}}
myClick(){
}
onClick = {this.myClick.bind(this)}
事件函数的this默认为undefined,所以要使用bind(this)修正this指向
如果使用create-react-app 有语法不用使用bind
下面的写法就不用加bind(this)
事件函数 = ()=> {}
类创建
class 组件名称(首字母大写) extends React.Component{
render(){
return (
JSX -> 结构
)
}
}
函数创建
function App(){
return (
<div>
<div>别的上面东西</div>
</div>
)
}
在react中的所有数据,只要是在state下的,这些数据发生变化就会让视图更新
constructor(){
super();
this.state = {
数据
}
}
this.setState({新数据})
第一个参数:可以是对象也可以是函数
当第一个参数为函数,函数的第一个参数为没改变的state,第二个参数为props,如果要改变状态得return一个对象
this.setState((prev)=>{return num:prev.num+1})
第二个参数:状态改变之后的回调
当有多个setState的时候,上面的state会合并成一个,只会执行最后一个
let {num}=this.state;
num++;
this.setState({num});
this.setState({num:5});
this.setState({num:6}); //只会执行这个
this.props.change(num);
定时器:
add=()=>{
setTimeout(()=>{
return this.setState({num:2});
console.log(this.state.num); //2同步
},10)
}
原声事件:
componentDidMount(){
let btn=document.querySelector('button');
btn.onclick=function(){
//原生事件
}
}
如果在表单元素上设置了一个(value、checked)默认值,那么该元素就变成了受控组件
不设置默认值
defaultValue 属性可设置或返回文本域的默认值。
1.在父组件上给子组件中绑定自定义属性,把数据放到自定义属性上(发送)
2.在子组件中使用this.props.自定义属性名(接收)
function fn(a){
//console.log(a)
a(function(a){
a(function(a){
console.log(a);
})
})
}
fn(function(a){
a(function(a){
a(a);
})
});
第一种情况(父级修改数据,传给子级)
父级的数据传到子级,数据本身还是父级的,如果用户操作子级要改变传递的数据,那么不能子级改,要让父级修改
也就是说,父级需要定义一个修改数据的方法,在传递数据的时候也一起传给子级
当触发子级行为的时候,子级去调用父级修改数据的方法,然后父级收到了子级的修改,父级修改数据,当父级的数据发生变化的时候,又把最新的数据传给了子级,完成数据的修改
第二种情况(子级修改数据,传给父级)
父级把数据给了子级,只想在触发子级的时候,子级的数据变,父级的数据不变
也就是说,父级通过自定义的方式传数据给子级,子级可以在constructor中接收父级传递的数据(就一次),把父级传递的这个数据,变为this.state(子级的),子级就拥有了父级的数据,并且修改自己的数据是不会影响父级
兄弟间组件通信,一般的思路就是找一个相同的父组件,这时候既可以用props传递数据,也可以用context的方式来传递数据。 当然也可以用一些全局的机制去实现通信,比如redux等。
https://www.jianshu.com/p/514fe21b9914
定义: 一个组件从开始到死亡的过程中会触发该生命周期中的事件,把这个事件的生命周期函数暴露出来给我们使用,这些(个)就叫做生命周期函数
简单来说:指在某一个时刻组件会自动调用执行的函数
monting 阶段(一次)
constructor √ 初始化数据时使用
componentWillMount 挂载之前 √ 组件即将被挂载到页面的时候自动执行
render 第一次渲染 √ 每一次渲染时要处理的逻辑(不能用this.setState( ))
componentDidMount 挂载之后 √ 请求数据、获取到真实的DOM
updating 阶段
shouldComponentUpdate 性能优化 组件被更新之前,他会自动被执行
如果写了必须写上一个布尔值,默认为true,当为false的时候updating阶段就停止,只有为true的时候才会被更新
componentWillUpdate 数据更新之前
组件被更新之前会自动执行,但是他在shouldComponentUpdate 之后执行,shouldComponentUpdate 返回true的时候才会执行
render 渲染
componentDidUpdate 组件、数据更新之后会被执行
注意:在上面这几个函数中不要使用this.setState( ),不然会死循环
componentWillReceiveProps 父级数据发生变化的时候使用(子组件)
如果这个组件第一次存在于父组件中,不会执行
如果这个组件之前已经存在于父组件中,才会执行
ummonting 阶段
componentWillUnmount 当组件死亡的时候触发(卸载、跳路由、关定时器、数据重置、变量制空,清除事件…)(子组件)
当这个组件即将被剔除的时候就会执行
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
//父级组件
class App extends Component {
constructor(props) {
super(props);
this.state = {
num:0
}
}
//monting 阶段
componentWillMount(){
//挂载之前
console.log('挂载之前');
}
componentDidMount(){
//挂载之后
console.log('挂载之后');
console.log(document.getElementById('box'));
}
//updating阶段
shouldComponentUpdate(nextProps){
//写了这个函数必须要有返回值
console.log('should');
return true;//默认为true,false就是不更新
}
componentWillUpdate(){
console.log('更新之前');
}
componentDidUpdate(){
console.log('更新之后');
}
// componentWillReceiveProps(nextProps){
// console.log('老爹发生变化的时候触发1');
// }
//点击事件方法
click=()=>{
let {num}=this.state;
num++;
this.setState({num});
}
//渲染
render() {
return (
<div id="box">
<button
onClick={this.click}
>{this.state.num}</button>
<hr />
<PPa num={this.state.num} />
</div>
// console.log(this.state)
);
}
}
//子级组件
class PPa extends Component {
constructor(props) {
super(props);
this.state = { }
}
componentWillReceiveProps(nextProps){
console.log('老爹发生变化的时候触发2');
}
render() {
return (
<div>子级{this.props.num}</div>
);
}
}
ReactDOM.render(<App />,document.getElementById('root'));
可以快速的获取组件或者元素
在指定组件上写一个ref的属性,值就随意
<App ref="app"/> 定义好了
this.refs.app 就能获得这个组件
安装 npm i prop-types -S
引包 import PropType from ‘prop-types’;
https://reacttraining.com/react-router/
/根 -> /user -> /post -> /get
http://www.baidu.com/home 一般默认找home文件夹下的index.html
http://www.baidu.com/works 一般默认找works文件夹下的index.html
安装:npm i react-router-dom -S
引包:import {BrowserRouter as Router |HashRouter as Router,Route} from ‘react-router-dom’
路由有2种
一种是hash路由 /#home
另一种history路由 /home
使用:必须把Router放到根下
ReactDOM.render((
<Router>
<App />
</Router>
),document.getbyId('xx'))
function App (){
return (
<Route path="/" component={App}/>
)
}
注意:
切换路由的时候可以通过组件内的props获取到路由信息
history 就是与H5的history一样,能够操作浏览器的历史记录
location 里面可以获取hash #, search ?, state:{key:value}
match 里面可以获取 url,path
<Link to="/home">首页</Link> to=”字符串”
<Link to={{pathhome:'/home',hash:'#age=1',search:'?num=1',state:{}}}></Link>
写法:
<Route path="/" component={组件} />
<Route path="/" component={()=>{return (<div>list</div>)}}/>
<Route path="/" render={(props)=>{return (<div>list</div>)}}/>
//props -> history、location、match
<Route path="/user:/id" /> 动态路由
在子页面就可以通过match.params.id去获取值,来改变我的数据或者样式…
exact
假设path=”/one”,
如果此时没有加上exact,那么能匹配到/one,还能匹配到/one/two
如果此时加上esact,那么能匹配到/one,/one/two是匹配不到的
/one /one/two true no
/one /one/two false yes
例子:
path="/" / -> home,如果不加eaxct,除了能匹配/,还能匹配/about,/xx
strict
当加上strict之后,path=”/one/” url上输入的是/one (少一个/ )不能匹配;
能匹配/one/或者/one/two
/one/ /one no
/one/ /one/ yes
/one/ /one/two yes
<Route path="/" render={(props)=>{
if(xx){
return (<Axx />)
}else{
return (<Redirect to="/xx"/>)
}
}}/>
<Switch>
<Route path="/about/d" component={AboutD}/>
<Route exact path="/about/:id" component={About2}/>
</Switch>
输入/about/d 会被优先捕获,捕获之后就不会在往下走了
设置
childContextTypes 定义函数数据中的数据类型(必须是组件的静态方法)
getchildContext 函数 用来设置数据的
获取
contextTypes 获取数据 子类下的静态方法
this.context.xxx 使用
注意:如果父级和祖先级都设置了一个一样的属性,会按就近原则执行
//在祖先辈定义
//App.childContextTypes={} //一般不这么写
static childContextTypes={//定义数据类型
num:PropTypes.number,
color:ProTypes.string //这里写几个就能用this.context拿到几个
}
getChildContext(){//函数 设置数据
renturn{
num:0
}
}
//孙子辈可以获取
static contextTypes={
num:PropTypes.number
}
this.context.num //直接这样使用
是通过React.createContext( ) -> Provider(生产),Consumer(消费)
let {Provider,Consumer}=React.createContext() //生成
<Provider value='哈哈'> //value={{key1:xx,key2:xx}}
<PaP />
</Provider>
function Fn(){
return (
<Consumer>//获取
{
(val)=>{
return(
<div>{val}</div>
)
}
}
</Consumer>
)
}
如果要让子组件享用context的数据为自己的状态,可以添加一个contextType=context的数据(React.createContext())
在constructor中没有this.context,在各生命周期中可以拿到(this.context拿)
import MyContext from './context';
class XX extends Component {
constructor(){
super();
this.state = {
}
}
static contextType = MyContext
componentDidMount(){
console.log(this.context);
}
}
安装 npm i redux -S
中间件:yarn add redux-thunk
引包 import {createStore} from 'redux'
创建store、reducer
定义reducer函数根据action的类型改变state
actions 定义指令
通过createStore创建store
调用store.dispatch()发出修改state的命令
createStore 创建store
store.dispatch 派发action传给store
store.getState 获取store里所有的数据内容
store.subScribe 订阅store的改变,只要store发生改变,subScribe这个函数接收的回调函数就会被执行
ADD_NUM 每次num + 1
CHANGE_NAME 把于海洋变成林同贺
function reducer(state={num:0,name:'于海洋'},action){
console.log(action); //ADD_NUM
switch(action.type){
case 'ADD_NUM':
return {...state,num:state.num+1}
case 'CHANGE_NAME':
return {...state,name:'林同贺'}
}
return state;
}
const store = createStore(reducer)
使用 console.log(store.getState()) -> {num:0}
修改state,就要触发dispatch({type:'ADD_NUM'}) ,默认传入一个对象
发起了action一定是要新给一个对象,如果你发现你修改了state,但是页面没有更新,问题基本上就是没有更新最新的状态
import {createStore,bindActionCreators} from 'redux';
import * as actionCreateors from './action';
const store = createStore(reducer);
const ActionCreators = bindActionCreators(actionCreateors,store.dispatch)
import { applyMiddleware, createStore } from 'redux';
import thunk from 'redux-thunk';
const store = createStore(
reducers,
applyMiddleware(thunk)
);
store/index.js
import {createStore,bindActionCreators} from 'redux';
const store = createStore(reducer)
const ActionCreators = bindActionCreators(actions,store.dispatch)
export {ActionCreators}
export default store
store/action.js
引入 actionTypes
export function incrment(){
return {type:actionTypes.INCRMENT}
}
....
store/actionTypes.js
const INCRMENT = 'INCRMENT';
....
store/reducer.js
export default function (state,action){
switch(action.type){
case xxx:
return 新的state
default:
return state
}
}
combineReducers({ //combineReducers 合并reducer的
reducer1:reducer1,
reducer2:reducer2
})
需要记的词:
store、action、reducer、dispatch、store.subscribe、getState()
bindActionCreators、combineReducers、actions、actionTypes
安装:npm install –save react-redux 或者 yarn add react-redux
React-Redux 提供Provider组件,能够使你的整个app访问到Redux store中的数据,Provider -> 放到顶层组件上
index.js
生产
import {Provider} from 'react-redux'; //引包
ReactDOM.render(
<Provider store={store}> //根组件
<App />
<PPa />
</Provider>,
document.getElementById('root'));
React-Redux提供一个connect方法能够让你把组件和store连接起来
components/app.js
import {connect} from 'react-redux';
function mapStateToProps(state){//state store的所有的数据
//可以过滤当前组件可用的数据
return {num:state.reducer1.num};
}
connect(mapstateProp,mapdispatchProp)(组件) 链接
//例:export default connect(mapStateToProps,actions)(App);
vue是MVVM框架,react是V框架,都专注于视图层
react 如果要写<div>,必须使用babel去转换React.crateClass()
vue<div>弱主张性 — 渐进式
angular 强主张性
MVVM框架
M-Model V-View VM-ViewModel(视图模型)
MVC:M-Model V-View C-Control
模板引擎 —SWIG EJS <%=IF(){}%>
{{}} 双声波写法 / 小胡子写法
new Vue({
el:'#挂载的元素名',
data:{
在new Vue中data的值是一个对象,对象里面就可以设置初始化的数据
}
})
v-if=”数据|条件” true 渲染在页面,false不渲染页面
v-else-if=”数据|条件”
v-else
注意:前一兄弟元素必须有 v-if 或 v-else-if。
如果触发频繁会影响性能
v-show=”条件” -> 成立:display:block 否则为none。尽量用这个
v-text -> innerText
v-html -> innerHTML
v-for=”val in data” 数组 val就是数组中的值
v-for=”(val,attr) in data”
如果是数组 val就是数组中的值,key就是索引
如果是对象,val值,attr名
事件绑定
v-on:不带on事件名
缩写:@ 比如:@click
修饰符:
.13 || .enter 回车
.stop 阻止冒泡
.prevent 阻止默认行为
….
事件函数应该写在 methods中
new Vue({
methods:{
方法1,
方法2
}
})
事件中可以绑定函数
click=”fn” fn没有参数,默认的参数是event
click=”fn(key)” fn如果有参数,那么key就是传进来的参数
如果又要传参又想获取event,那么需要在函数调用中写* $event *
click=”fn(key,$event)”
v-bind 绑定行间属性
缩写: :
如果属性为value=”“,src=”“,href=”“… 里面的值是静态的(写死那么不需要v-bind)
如果属性的值是通过数据得到,那么需要加上v-bind
v-bind:value
v-bind:href
v-bind:src
v-bind:style
...
缩写为
:value=""
:href=""
....
v-model 只要是表单就要立马想到是否使用v-model (双向数据绑定)
v-model=”数据”
这个数据要么直接是data下的属性,要么就一定是一个引用类型下的属性
computed:计算属性
如果需要通过data的数据派生出别的结果,这个时候就要想到computed并且还要让第一次运行,接下来每次修改接着运行
computed:{
/*
代码一上来先执行一次,然后只要数据发生变化就触发
*/
fn(){
return this.arr.every(item=>item)
},
all:{//all就是get的返回值
get(){
return this.arr.every(item=>item)
},
set(val){
val 就是当修改数据时的值
}
}
}
数据劫持
let obj = {
num:2
}
let v = 1;
//去设置属性
Object.defineProperty(obj,'n',{
// value:0,
get(){
console.log('拦截获取'); //读操作
return v*=2; //读的时候进行拦截
},
// set(val){ //写属性的时候进行拦截
// // console.log(val);
// v = val;
// }
// writable:true, //是否能够修改这个属性,默认为不能修改
// enumerable:true,//n这个属性是否能被枚举
// configurable:true,//是否能被删除
// writable:false
});
//在操作某个属性的时候,进行拦截
// obj.n = 10;
// delete obj.n;
// console.log(obj.n);
// obj.n = 30;
// for(let attr in obj){
// console.log(attr);
// }
// console.log(obj.n);
console.log(obj.n < 3 && obj.n >= 4); //true
watch:监听data数据的变化,只要变化就触发,第一次是不会触发的,只有改变后才触发
写法:
data:{
arr:[]
},
watch:{
要监听的数据名:函数或者对象
比如:
arr(newValue,oldValue){
//当arr发生变化的时候做得事情
}
如果数据要深层监听要用对象的方式
比如:
arr:{
handler:function(newValue,oldValue){
//当arr发生变化的时候做得事情
},
deep:true
}
}
在Vue中有一个component的方法来创建组件。
注意:方法必须写在new Vue的上面;
要记得插入子组件;
data的值为函数,这个函数必须返回一个对象,对象的值就是初始化数据
组件的名字要么小写,要么烤串命名:get-by-id
组件顶层只能有一个元素
使用:
<div id="app">
{{num}}
<ppa></ppa>
</div>
<script>
Vue.component('ppa',{
template:'<div>子组件{{nn}}</div>',
data(){
return{
ary:[],
nn:10
}
}
});
new Vue({
el:'#app',
data:{
num:0
}
});
</script>
react是单向的流动
vue是单向流动 -> 父级的数据传递给子级,如果需要通过操作子级修改数据,只能是父级修改(因为数据是从父级流向子级的,子级是不能流动数据给父级的)
双向数据绑定 -> 数据驱动视图,视图操作数据
父传子:
1.通过在子组件上挂一个自定义属性
如果传递的值是一个静态的,那么不需要加v-bind
比如:pn=”1” 这个1就是静态的
如果传递的值是一个动态的,那么这个属性必须是被v-bind所绑定
比如::pn=”num” 这个num是父级或者可能需要变化的
2.接收:
子组件写上props的属性
比如:
{
data(){
return{}
},
props:['自定义属性的名字'] 可以是个数组
还可以写对象(对象是为了验证传递的数据类型)
props:{
自定义的名字:类型
pn:String
}
还可以
props:{
自定义的名字:[类型,类型]
pn:[String,Number]
还可以写
default:100 // 如果没有传默认值为100
required:true //必须传
}
}
子传父:
第一种:子级和父级同时改变
1.父级定义一个改变数据的方法
2.子组件内使用this,$emit(‘通道’)去定义一个自定义事件
比如:this.$emit(‘tongdao’,可以加参数)
3.在子组件上绑定tongdao这个事件,并且把父组件的改变数据的方法添加到自定义事件上
第二种:把父级的数据变成子级自己的,跟父级断开联系
1.通过props去接收父级的数据
2.把接收到的数据存到自己的data中
{
props:['nn'],
data(){
return {
cnum:this.nn
}
},
template:`<div>{{cnum}}</div>`
}
如果想父级的数据根据子级的数据变化;那么,父级需要定义的一个方法去接收子级传过来的数据
比如:
getchildfn(val){
this.num = val;
}
然后通过事件绑定的方式把getchildfn传给子级
@自定义名字="getchildfn"
最后当修改子级数据的时候,创建一个自定义名字的事件,并且把数据传进去
比如:
this.$emit('自定义名字',要传给父级的数据)
给元素身上加上ref的属性,值就是自定义的名字
小技巧:
在v-for的时候如果在被遍历的元素上写死一个名字,那么所有的遍历元素都会有相同的名字,那么这个时候可以使用字符串拼接的方式来区分ref的名字
比如:
<li v-for="val,key in arr" ref="'aa'+val.id"></li> -> refs['aa'+id]
当DOM加载完成执行的一个函数(在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。)
在组件中使用slot标签,包裹默认的元素和样式 || 组件
用双标签把子级组件注入到父级组件中,那么显示出来的结果就是slot标签中的结构 || 组件
如果在双标签中有添加别的标签,那么就会显示添加的标签而不会显示slot中的默认标签 || 组件
比如:
走slot默认
<div id="root">
<ppa></ppa>
</div>
<script>
</script>
走自定义的组件
<div id="root">
<ppa>
<p>自定义组件</p>
</ppa>
</div>
<script>
</script>
比如:
<div id="root">
<ppa>
<p slot="m2">自定义组件2</p>
<p slot="m1">自定义组件</p>
</ppa>
</div>
<script>
</script>
当new了Vue之后就执行vue的代码
beforeCreate 数据挂载之前 拿不到数据
created 数据挂载之后 可以拿到数据,此时可以进行ajax请求
有没有el(挂载元素),如果有去看有没有模板,如果没有看看有没有$mount(‘el’),如果都没有就没有后续了,页面该是什么样子就是什么样子
beforeMount (数据渲染之前) 数据还没生产模板,只能获取到页面中手写的DOM
mounted (数据渲染之后) 在这个生命周期中,我们可以获取到渲染之后的DOM
上面的过程只会执行一次
beforeUpdate 数据修改更新之前
updated 数据更新之后,可以获取到最新的数据和数据对应的dom
beforeUpdate、updated 只要数据发生变化就执行
beforeDestroy 组件卸载之前 关闭定时器,函数初始化
destroyed 销毁监听事件
<div id="app">
<div id="box"
@click="++num"
><div v-for="val in arr">{{val}}</div></div>
</div>
<script src="./vue.js"></script>
<script>
let vm = new Vue({
// el:'#app',
//数据加载之前,是获取不到数据的
beforeCreate() {
console.log(this.num);
},
//数据加载之后,可以获取到数据
created(){ //ajax请求
setTimeout(() => {
this.num = 10;
}, 1000);
// console.log(this.num);
},
beforeMount() {
//模板加载数据之前
console.log('之前',document.getElementById('box').children);
},
mounted() {
//模板加载数据之后
console.log('之后',document.getElementById('box').children);
},
beforeUpdate() {
console.log('数据改变之前');
},
updated(){
console.log('数据改变之后');
},
data:{
num:0,
arr:[1,2,3,4]
}
}).$mount('#app')
</script>
安装 vue-router
创建一个router文件夹
router / index.js
import VueRouter from 'vue-router';
Vue.use(Router)
const routes = [
]
const router = new VueRouter({
mode:'history', //开启history模式,不写默认hash模式
routes
})
export default router
src / main.js
import VueRouter from 'vue-router';
import router from './router/index';
new Vue({
router
})
用路由一定要使用:<router-view></router-view>
配置好路由之后,可以通过$route.xx去拿到查询信息或者paramsId
比如:
模板中:
$route.params.id
$route.query.a
组件中:
this.$route.params.id
this.$route.query.a
<router-link to="/">
<router-link :to="{path:'/',query:{a:1}}">
<router-link :to="{name:'news',query:{a:1}}">
$route是放属性的
可以获取到查询信息: ?后面的
可以获取hash值:#后面的
可以获取到params /xx/:id
$router是放方法的
push()
replace
go(1)
需要在router.beforeEach((to,from,next)=>{
to,//到哪里去
from,//从哪里来
next,//继续向下执行
next();
});
router.afterEach((to,from)=>{
//路由守卫结束
});
beforeRouteEnter((to,from,next)=>{
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
})
beforeRouteUpdate((to,from,next)=>{
// 在当前路由改变,但是该组件***被复用时调用***
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
})
beforeRouteLeave((to,from,next)=>{
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
})
const router = new VueRouter({
routes: [
{ path: '/a', redirect: '/b' }
]
})
new Vue() vue的实例就是EVENT BUS
绑定事件:
在created下去写一个事件绑定
Bus.$on(‘事件名’,(传入参数)=>{该事件要做的事情})
事件触发:
在methods中触发
Bus.$emit(‘要触发的事件名’,参数)
安装包 npm install vuex -S
引包
import Vuex from ‘vuex’;
Vue.use(Vuex);引入中间件
创建store
const store = new Vuex.Store({
state:{
//初始化数据
},
mutations:{
//数据更新的方法都写在里面,只有mutations才能改变数据的状态
increment(state){
state.num ++;
}
}
});
在new Vue({store}) 引入store
Vue组件如何显示store的数据呢?
如果在模板中
$store.state.xx
如果在组件的方法中
this.$store.state.xxx
还可以通过vuex下的mapState去获取数据
在computed下使用mapState
比如:
import {mapState} from 'vuex';
computed:mapState({
nn:'num' 把store下的num获取到,并且取个nn的名字
nn:state=>state.num 效果和上面一样
fn(state){
return state.num + this.num; //this代表当前组件的
}
})
或者是
...mapState(['num'])
<!-- let {num.str...} = this.props -->
getters: 就是通过数据派生出来的结果
比如:
import {mapState,mapGetters} from 'vuex';
computed:...mapState(['toudbl'])
mutations:是改变store中状态的唯一方式;Mutation 必须是同步函数
mutations:{
add(state,n){
state.num += n;
}
}
发起mutations使用commit
$stroe.commit('add',5);
actions:异步请求的时候使用
actions:{
actionAdd(context){
setTimeout(() => {
context.commit('add');
},2000)
}
}
$store.dispatch('actionAdd')
mapActions:
methods:{
...mapActions(['actionAdd'])
}
https://robomongo.org/download robo 3T/Studio 3T
nodemon 自动更新代码
npm install nodemon -g 全局安装nodemon
node server.js
nodemon server.js 安装成功后用这个命令启动
const conn = mongoose.createConnection('mongodb://127.0.0.1:27017/数据库的名字',{
useNewUrlParser:true
})
conn.on('connected',()={
//连接成功的时候触发
})
conn.on('error',()={
//连接失败的时候触发
})
conn.on('open',()={
//数据库打开的时候触发
})
new moogoose.Schema({user:String,ary:[],title:{}})
注意:设计骨架的时候可以添加必填设置
new mongoose.Schema({
user:String,
ary:[],
pass:{
type:String,
required:true //必填项
},
zhushi:{
default:'哈哈'
}
})
conn.model(‘模型名’,骨架)
增:create 删:delete 改:update 查:find
增加数据:通过模型去创建真实的数据,比如:
const UserModel = conn.model('User',UserSchema);
1.UserModel.create({user:'xxx',pass:123},(error,data)=>{
})
2.UserModel.create({user:'xxx',pass:123}).then((data)=>{})
3.;(async function(){
const d = await UserModel.create({user:'xxx',pass:123})
})()
在创建数据的时候,如果骨架中没有添加这个数据字段,那么这项数据是不会成功添加进数据库的,骨架中有数据字段只能减少不能增多,比如:
UserModel.create({user:'xxx',pass:123,age:12})
这里的age是没有的在骨架中的,所以age是不能成功添加到数据库中的
查询:find,findOne,findById
find就是查多个数据,返回的是数组
find({_id:12321321,age:18}) 查所有的数据
find({id:12321321,age:18},{user:1,id:0}) 查所有的数据
返回的数组,user显示,id就不显示
findOne就是查询一个数据
findById(‘321312’)